From 1c6371ccdb91bb33999566fcbf790aed865f1cb3 Mon Sep 17 00:00:00 2001 From: Hyunwoo Jung Date: Mon, 17 Nov 2025 22:11:34 +0900 Subject: [PATCH 1/3] Apply spring-javaformat-maven-plugin Signed-off-by: Hyunwoo Jung --- pom.xml | 4 + .../aws/AWSCompanionAutoConfiguration.java | 6 +- .../function/adapter/aws/AWSLambdaUtils.java | 44 +- .../adapter/aws/AWSTypesMessageConverter.java | 32 +- .../adapter/aws/CustomRuntimeEventLoop.java | 55 +- .../adapter/aws/CustomRuntimeInitializer.java | 3 +- .../function/adapter/aws/FunctionInvoker.java | 37 +- .../adapter/test/aws/AWSCustomRuntime.java | 16 +- .../aws/CustomRuntimeEventLoopTest.java | 174 ++- .../adapter/aws/FunctionInvokerTests.java | 1015 ++++++----------- .../function/adapter/aws/TestContext.java | 1 - .../azure/web/AzureWebProxyInvoker.java | 25 +- .../azure/web/AzureWebProxyInvokerTests.java | 19 +- .../cloud/function/adapter/azure/web/Pet.java | 5 + .../function/adapter/azure/web/PetData.java | 8 +- .../azure/web/PetStoreSpringAppConfig.java | 9 +- .../adapter/azure/web/PetsController.java | 2 + .../azure/AzureFunctionInstanceInjector.java | 34 +- .../adapter/azure/AzureFunctionUtil.java | 9 +- .../adapter/azure/helper/BuilderStub.java | 2 + .../azure/helper/HttpRequestMessageStub.java | 5 + .../azure/helper/HttpResponseMessageStub.java | 5 +- .../AzureFunctionInstanceInjectorTest.java | 14 +- ...ionInstanceInjectorServiceLoadingTest.java | 17 +- .../function/adapter/gcp/FunctionInvoker.java | 29 +- .../function/adapter/gcp/GcfJarLauncher.java | 10 +- .../adapter/gcp/layout/GcfJarLayout.java | 4 +- .../gcp/layout/GcfJarLayoutFactory.java | 1 + .../gcp/FunctionInvokerBackgroundTests.java | 12 +- .../adapter/gcp/FunctionInvokerHttpTests.java | 27 +- .../FunctionInvokerIntegrationTests.java | 9 +- .../integration/LocalServerTestSupport.java | 4 +- .../ce/CloudEventGrpcAutoConfiguration.java | 2 +- .../function/grpc/ce/CloudEventHandler.java | 9 +- .../grpc/ce/CloudEventMessageConverter.java | 16 +- ...unctionGrpcCloudeventApplicationTests.java | 6 +- .../serverless/web/AWSTypesProcessor.java | 18 +- .../serverless/web/FunctionClassUtils.java | 31 +- .../web/ServerlessAsyncContext.java | 22 +- .../web/ServerlessAutoConfiguration.java | 6 +- .../web/ServerlessFilterRegistration.java | 1 - .../web/ServerlessHttpServletRequest.java | 65 +- .../web/ServerlessHttpServletResponse.java | 57 +- .../serverless/web/ServerlessHttpSession.java | 1 - .../serverless/web/ServerlessMVC.java | 82 +- .../web/ServerlessServletContext.java | 11 +- .../web/ServerlessServletRegistration.java | 5 +- .../web/ServerlessWebApplication.java | 31 +- .../serverless/web/AsyncStartTests.java | 9 +- .../serverless/web/RequestResponseTests.java | 27 +- .../web/ServerlessWebServerFactoryTests.java | 1 + .../test/app/FreemarkerController.java | 1 + .../cloud/function/test/app/Pet.java | 5 + .../cloud/function/test/app/PetData.java | 7 +- .../test/app/PetStoreSpringAppConfig.java | 29 +- .../function/test/app/PetsController.java | 6 +- .../function/actuator/FunctionsEndpoint.java | 10 +- .../cloudevent/CloudEventHeaderEnricher.java | 11 +- .../cloudevent/CloudEventMessageBuilder.java | 37 +- .../cloudevent/CloudEventMessageUtils.java | 112 +- ...dEventsFunctionExtensionConfiguration.java | 7 +- .../CloudEventsFunctionInvocationHelper.java | 38 +- .../context/DefaultMessageRoutingHandler.java | 14 +- .../function/context/FunctionCatalog.java | 60 +- .../function/context/FunctionProperties.java | 47 +- .../context/FunctionRegistration.java | 45 +- .../context/FunctionTypeProcessor.java | 29 +- .../context/FunctionalSpringApplication.java | 39 +- .../context/MessageRoutingCallback.java | 25 +- .../cloud/function/context/PollableBean.java | 35 +- .../context/PostProcessingFunction.java | 38 +- .../BeanFactoryAwareFunctionRegistry.java | 130 ++- .../catalog/FunctionAroundWrapper.java | 14 +- .../context/catalog/FunctionTypeUtils.java | 231 ++-- .../context/catalog/HeaderEnricher.java | 8 +- .../catalog/SimpleFunctionRegistry.java | 582 +++++----- ...ntextFunctionCatalogAutoConfiguration.java | 60 +- .../ContextFunctionCatalogInitializer.java | 35 +- .../context/config/FunctionContextUtils.java | 26 +- .../FunctionsEndpointAutoConfiguration.java | 3 +- .../context/config/JsonMessageConverter.java | 14 +- ...tlinLambdaToFunctionAutoConfiguration.java | 77 +- .../config/MessageConverterHelper.java | 20 +- .../context/config/RoutingFunction.java | 66 +- .../SmartCompositeMessageConverter.java | 66 +- .../context/message/MessageUtils.java | 11 +- .../test/FunctionalSpringBootTest.java | 1 - .../cloud/function/json/JacksonMapper.java | 6 +- .../cloud/function/json/JsonMapper.java | 16 +- .../DefaultFunctionObservationConvention.java | 4 +- .../observability/FunctionContext.java | 1 + .../observability/FunctionObservation.java | 1 + .../FunctionObservationConvention.java | 1 + .../ObservationAutoConfiguration.java | 6 +- .../ObservationFunctionAroundWrapper.java | 15 +- .../function/utils/FunctionClassUtils.java | 31 +- .../function/utils/FunctionMessageUtils.java | 6 +- .../cloud/function/utils/JsonMasker.java | 3 +- .../cloud/function/utils/KotlinUtils.java | 2 + ...mitiveTypesFromStringMessageConverter.java | 7 +- .../cloud/function/utils/SocketUtils.java | 1 + .../actuator/FunctionsEndpointTests.java | 9 +- .../cloudevent/CloudEventFunctionTests.java | 171 ++- ...CloudEventMessageUtilsAndBuilderTests.java | 69 +- .../cloudevent/SpringReleaseEvent.java | 4 +- .../function/context/HeaderMappingTests.java | 168 +-- .../HybridFunctionalRegistrationTests.java | 38 +- .../context/MessageRoutingCallbackTests.java | 19 +- ...yAwareFunctionRegistryMultiInOutTests.java | 193 ++-- ...BeanFactoryAwareFunctionRegistryTests.java | 271 +++-- ...FactoryAwarePojoFunctionRegistryTests.java | 38 +- .../catalog/FunctionTypeUtilsTests.java | 143 ++- .../catalog/SimpleFunctionRegistryTests.java | 336 +++--- ...oConfigurationConditionalLoadingTests.java | 31 +- ...FunctionCatalogAutoConfigurationTests.java | 213 ++-- ...ontextFunctionCatalogInitializerTests.java | 107 +- .../config/JsonMessageConverterTests.java | 13 +- .../context/config/RoutingFunctionTests.java | 67 +- .../string/FunctionalStringSourceTests.java | 6 +- .../context/test/FunctionalTests.java | 3 +- .../cloud/function/scan/ScannedFunction.java | 8 +- .../cloud/function/test/GenericFunction.java | 5 +- .../function/userissues/UserIssuesTests.java | 37 +- .../cloud/function/utils/JsonMapperTests.java | 17 +- .../cloud/function/utils/JsonMaskerTests.java | 201 +--- .../core/FunctionInvocationHelper.java | 4 +- .../dsl/FunctionFlowAutoConfiguration.java | 5 +- .../integration/dsl/FunctionFlowBuilder.java | 22 +- .../dsl/FunctionFlowDefinition.java | 13 +- .../integration/dsl/FunctionLookupHelper.java | 13 +- .../integration/dsl/FunctionFlowTests.java | 42 +- ...ogAutoConfigurationKotlinSuspendTests.java | 21 +- ...onCatalogAutoConfigurationKotlinTests.java | 47 +- .../kotlin/KotlinTypeDiscoveryTests.java | 3 +- .../function/web/BasicStringConverter.java | 12 +- .../function/web/FunctionHttpProperties.java | 24 +- .../web/constants/WebRequestConstants.java | 12 +- .../function/web/flux/FunctionController.java | 83 +- .../web/flux/FunctionHandlerMapping.java | 13 +- .../web/flux/ReactorAutoConfiguration.java | 3 +- .../function/FunctionEndpointInitializer.java | 57 +- .../function/web/mvc/FunctionController.java | 73 +- .../web/mvc/FunctionHandlerMapping.java | 19 +- .../web/mvc/ReactorAutoConfiguration.java | 5 +- .../web/source/ExporterProperties.java | 4 +- .../FunctionExporterAutoConfiguration.java | 2 +- .../source/FunctionExporterInitializer.java | 21 +- .../function/web/source/HttpSupplier.java | 18 +- .../web/source/SimpleRequestBuilder.java | 3 +- .../function/web/source/SupplierExporter.java | 76 +- .../FunctionWebRequestProcessingHelper.java | 51 +- .../function/web/util/FunctionWrapper.java | 3 +- .../cloud/function/web/util/HeaderUtils.java | 10 +- .../flux/FluxRestApplicationTests.java | 191 ++-- .../function/mvc/MvcRestApplicationTests.java | 157 ++- .../test/ExplicitNonFunctionalTests.java | 14 +- .../test/FunctionalExporterTests.java | 28 +- .../cloud/function/test/FunctionalTests.java | 10 +- .../test/FunctionalWithInputListTests.java | 17 +- .../test/FunctionalWithInputSetTests.java | 19 +- .../function/test/HeadersToMessageTests.java | 28 +- .../test/ImplicitNonFunctionalTests.java | 10 +- .../MoreThenOneFunctionRootMappingTests.java | 13 +- .../cloud/function/test/PojoTests.java | 24 +- .../web/flux/HeadersToMessageTests.java | 35 +- .../web/flux/HttpGetIntegrationTests.java | 137 +-- .../web/flux/HttpPostIntegrationTests.java | 246 ++-- .../cloud/function/web/flux/PrefixTests.java | 13 +- .../function/web/flux/SingletonTests.java | 13 +- .../FunctionEndpointInitializerMVCTests.java | 18 +- .../FunctionEndpointInitializerTests.java | 47 +- .../function/HeadersResponseMappingTests.java | 6 +- .../web/function/UserSubmittedTests.java | 11 +- .../function/web/mvc/DefaultRouteTests.java | 7 +- .../web/mvc/GeneralIntegrationTests.java | 58 +- .../web/mvc/HeadersToMessageTests.java | 38 +- .../web/mvc/HttpDeleteIntegrationTests.java | 21 +- .../web/mvc/HttpGetIntegrationTests.java | 138 +-- .../web/mvc/HttpPostIntegrationTests.java | 222 ++-- .../function/web/mvc/MultipartFileTests.java | 4 +- .../cloud/function/web/mvc/PrefixTests.java | 16 +- .../web/mvc/RoutingFunctionTests.java | 110 +- .../function/web/mvc/SingletonTests.java | 13 +- ...tionAutoConfigurationIntegrationTests.java | 10 +- ...figurationWithRetriesIntegrationTests.java | 13 +- .../web/source/WebAppIntegrationTests.java | 10 +- 186 files changed, 4246 insertions(+), 4353 deletions(-) diff --git a/pom.xml b/pom.xml index 630bfbf6a..06538d029 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,10 @@ + + io.spring.javaformat + spring-javaformat-maven-plugin + org.apache.maven.plugins maven-checkstyle-plugin diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSCompanionAutoConfiguration.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSCompanionAutoConfiguration.java index 54b8140f8..9ec7f1a87 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSCompanionAutoConfiguration.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSCompanionAutoConfiguration.java @@ -25,9 +25,7 @@ import org.springframework.context.support.GenericApplicationContext; import org.springframework.util.CollectionUtils; - /** - * * @author Oleg Zhurakousky * @since 3.2 * @@ -38,8 +36,8 @@ public class AWSCompanionAutoConfiguration { @Bean public AWSTypesMessageConverter awsTypesMessageConverter(GenericApplicationContext applicationContext) { JsonMapper jsonMapper = CollectionUtils.isEmpty(applicationContext.getBeansOfType(JsonMapper.class).values()) - ? new JacksonMapper(new ObjectMapper()) - : applicationContext.getBean(JsonMapper.class); + ? new JacksonMapper(new ObjectMapper()) : applicationContext.getBean(JsonMapper.class); return new AWSTypesMessageConverter(jsonMapper); } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java index 278902bb6..d3d3f8840 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java @@ -42,7 +42,6 @@ import org.springframework.util.StreamUtils; /** - * * @author Oleg Zhurakousky * @author Anton Barkan * @@ -79,13 +78,13 @@ static boolean isSupportedAWSType(Type type) { type = FunctionTypeUtils.getImmediateGenericType(type, 0); } Class rawType = FunctionTypeUtils.getRawType(type); - return rawType != null && rawType.getPackage() != null && - rawType.getPackage().getName().startsWith( - "com.amazonaws.services.lambda.runtime.events"); + return rawType != null && rawType.getPackage() != null + && rawType.getPackage().getName().startsWith("com.amazonaws.services.lambda.runtime.events"); } @SuppressWarnings("rawtypes") - public static Message generateMessage(InputStream payload, Type inputType, boolean isSupplier, JsonMapper jsonMapper, Context context) throws IOException { + public static Message generateMessage(InputStream payload, Type inputType, boolean isSupplier, + JsonMapper jsonMapper, Context context) throws IOException { if (inputType != null && FunctionTypeUtils.isMessage(inputType)) { inputType = FunctionTypeUtils.getImmediateGenericType(inputType, 0); } @@ -101,7 +100,8 @@ public static Message generateMessage(InputStream payload, Type inputType, boole } } - public static Message generateMessage(byte[] payload, Type inputType, boolean isSupplier, JsonMapper jsonMapper) { + public static Message generateMessage(byte[] payload, Type inputType, boolean isSupplier, + JsonMapper jsonMapper) { return generateMessage(payload, inputType, isSupplier, jsonMapper, null); } @@ -124,10 +124,8 @@ public static Message generateMessage(byte[] payload, Type inputType, bo Message requestMessage; - MessageBuilder builder = MessageBuilder - .withPayload(structMessage instanceof Map msg && msg.containsKey("payload") - ? (msg.get("payload")) - : payload); + MessageBuilder builder = MessageBuilder.withPayload( + structMessage instanceof Map msg && msg.containsKey("payload") ? (msg.get("payload")) : payload); if (isApiGateway) { builder.setHeader(AWSLambdaUtils.AWS_API_GATEWAY, true); if (JsonMapper.isJsonStringRepresentsCollection(((Map) structMessage).get("body"))) { @@ -165,7 +163,8 @@ private static Object convertFromJsonIfNecessary(Object value, JsonMapper object } @SuppressWarnings("unchecked") - public static byte[] generateOutputFromObject(Message requestMessage, Object output, JsonMapper objectMapper, Type functionOutputType) { + public static byte[] generateOutputFromObject(Message requestMessage, Object output, JsonMapper objectMapper, + Type functionOutputType) { Message responseMessage = null; if (output instanceof Publisher) { List result = new ArrayList<>(); @@ -209,25 +208,27 @@ else if (result.size() > 1) { } @SuppressWarnings({ "rawtypes", "unchecked" }) - public static byte[] generateOutput(Message requestMessage, Message responseMessage, - JsonMapper objectMapper, Type functionOutputType) { + public static byte[] generateOutput(Message requestMessage, Message responseMessage, JsonMapper objectMapper, + Type functionOutputType) { if (isSupportedAWSType(functionOutputType)) { return extractPayload((Message) responseMessage, objectMapper); } - byte[] responseBytes = responseMessage == null ? "\"OK\"".getBytes() : extractPayload((Message) responseMessage, objectMapper); - if (requestMessage.getHeaders().containsKey(AWS_API_GATEWAY) && ((boolean) requestMessage.getHeaders().get(AWS_API_GATEWAY))) { + byte[] responseBytes = responseMessage == null ? "\"OK\"".getBytes() + : extractPayload((Message) responseMessage, objectMapper); + if (requestMessage.getHeaders().containsKey(AWS_API_GATEWAY) + && ((boolean) requestMessage.getHeaders().get(AWS_API_GATEWAY))) { Map response = new HashMap(); - response.put(IS_BASE64_ENCODED, responseMessage != null && responseMessage.getHeaders().containsKey(IS_BASE64_ENCODED) - ? responseMessage.getHeaders().get(IS_BASE64_ENCODED) : false); + response.put(IS_BASE64_ENCODED, + responseMessage != null && responseMessage.getHeaders().containsKey(IS_BASE64_ENCODED) + ? responseMessage.getHeaders().get(IS_BASE64_ENCODED) : false); AtomicReference headers = new AtomicReference<>(); int statusCode = HttpStatus.OK.value(); if (responseMessage != null) { headers.set(responseMessage.getHeaders()); - statusCode = headers.get().containsKey(STATUS_CODE) - ? (int) headers.get().get(STATUS_CODE) + statusCode = headers.get().containsKey(STATUS_CODE) ? (int) headers.get().get(STATUS_CODE) : HttpStatus.OK.value(); } @@ -237,8 +238,8 @@ public static byte[] generateOutput(Message requestMessage, Message responseM response.put("statusDescription", httpStatus.toString()); } - String body = responseMessage == null - ? "\"OK\"" : new String(extractPayload((Message) responseMessage, objectMapper), StandardCharsets.UTF_8); + String body = responseMessage == null ? "\"OK\"" : new String( + extractPayload((Message) responseMessage, objectMapper), StandardCharsets.UTF_8); response.put(BODY, body); if (responseMessage != null) { Map responseHeaders = new HashMap<>(); @@ -259,4 +260,5 @@ public static byte[] generateOutput(Message requestMessage, Message responseM private static boolean isRequestKinesis(Message requestMessage) { return requestMessage.getHeaders().containsKey("Records"); } + } 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 35126ccb4..f0197b34f 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 @@ -37,11 +37,10 @@ import org.springframework.util.MimeType; /** - * Implementation of {@link MessageConverter} which uses Jackson or Gson libraries to do the - * actual conversion via {@link JsonMapper} instance. + * Implementation of {@link MessageConverter} which uses Jackson or Gson libraries to do + * the actual conversion via {@link JsonMapper} instance. * * @author Oleg Zhurakousky - * * @since 3.2 */ class AWSTypesMessageConverter extends JsonMessageConverter { @@ -52,8 +51,9 @@ class AWSTypesMessageConverter extends JsonMessageConverter { 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")); + this(jsonMapper, new MimeType("application", "json"), + new MimeType(CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getType(), + CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getSubtype() + "+json")); } AWSTypesMessageConverter(JsonMapper jsonMapper, MimeType... supportedMimeTypes) { @@ -69,9 +69,10 @@ protected boolean canConvertFrom(Message message, @Nullable Class targetCl if (message.getHeaders().containsKey(AWSLambdaUtils.AWS_EVENT)) { return ((boolean) message.getHeaders().get(AWSLambdaUtils.AWS_EVENT)); } - //TODO Do we really need the ^^ above? It seems like the line below dows the trick - else if (targetClass.getPackage() != null && - targetClass.getPackage().getName().startsWith("com.amazonaws.services.lambda.runtime.events")) { + // TODO Do we really need the ^^ above? It seems like the line below dows the + // trick + else if (targetClass.getPackage() != null + && targetClass.getPackage().getName().startsWith("com.amazonaws.services.lambda.runtime.events")) { return true; } return false; @@ -82,9 +83,10 @@ 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()); + if (targetClass.getPackage() != null + && targetClass.getPackage().getName().startsWith("com.amazonaws.services.lambda.runtime.events")) { + PojoSerializer serializer = LambdaEventSerializers.serializerFor(targetClass, + Thread.currentThread().getContextClassLoader()); Object event = serializer.fromJson(new ByteArrayInputStream((byte[]) message.getPayload())); return event; } @@ -115,24 +117,24 @@ protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) return true; } - @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)) { + 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())); + 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 7a63075b7..a1a3dd1ba 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 @@ -55,8 +55,8 @@ import static org.apache.http.HttpHeaders.USER_AGENT; /** - * Event loop and necessary configurations to support AWS Lambda - * Custom Runtime - https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html. + * Event loop and necessary configurations to support AWS Lambda Custom Runtime - + * https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html. * * @author Oleg Zhurakousky * @author Mark Sailes @@ -69,13 +69,15 @@ public final class CustomRuntimeEventLoop implements SmartLifecycle { private static Log logger = LogFactory.getLog(CustomRuntimeEventLoop.class); static final String LAMBDA_VERSION_DATE = "2018-06-01"; + private static final String LAMBDA_ERROR_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/{2}/error"; + private static final String LAMBDA_RUNTIME_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/next"; + private static final String LAMBDA_INVOCATION_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/{2}/response"; - private static final String USER_AGENT_VALUE = String.format( - "spring-cloud-function/%s-%s", - System.getProperty("java.runtime.version"), - extractVersion()); + + private static final String USER_AGENT_VALUE = String.format("spring-cloud-function/%s-%s", + System.getProperty("java.runtime.version"), extractVersion()); private final ConfigurableApplicationContext applicationContext; @@ -125,7 +127,9 @@ private void eventLoop(ConfigurableApplicationContext context) { logger.debug("Event URI: " + eventUri); } - RequestEntity requestEntity = RequestEntity.get(URI.create(eventUri)).header(USER_AGENT, USER_AGENT_VALUE).build(); + RequestEntity requestEntity = RequestEntity.get(URI.create(eventUri)) + .header(USER_AGENT, USER_AGENT_VALUE) + .build(); FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); RestTemplate rest = new RestTemplate(); JsonMapper mapper = context.getBean(JsonMapper.class); @@ -144,18 +148,22 @@ private void eventLoop(ConfigurableApplicationContext context) { if (response != null && response.hasBody()) { String requestId = response.getHeaders().getFirst("Lambda-Runtime-Aws-Request-Id"); try { - FunctionInvocationWrapper function = locateFunction(environment, functionCatalog, response.getHeaders()); + 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, clientContext); + ByteArrayInputStream is = new ByteArrayInputStream( + response.getBody().getBytes(StandardCharsets.UTF_8)); + Message requestMessage = AWSLambdaUtils.generateMessage(is, function.getInputType(), + function.isSupplier(), mapper, clientContext); requestMessage = enrichTraceHeaders(response.getHeaders(), requestMessage); Object functionResponse = function.apply(requestMessage); - byte[] responseBytes = AWSLambdaUtils.generateOutputFromObject(requestMessage, functionResponse, mapper, function.getOutputType()); + byte[] responseBytes = AWSLambdaUtils.generateOutputFromObject(requestMessage, functionResponse, + mapper, function.getOutputType()); - String invocationUrl = MessageFormat - .format(LAMBDA_INVOCATION_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE, requestId); + String invocationUrl = MessageFormat.format(LAMBDA_INVOCATION_URL_TEMPLATE, runtimeApi, + LAMBDA_VERSION_DATE, requestId); ResponseEntity result = rest.exchange(RequestEntity.post(URI.create(invocationUrl)) .header(USER_AGENT, USER_AGENT_VALUE) @@ -179,9 +187,7 @@ private Message enrichTraceHeaders(HttpHeaders headers, Message message) { String headerTrace = trim(headers.getFirst("X-Amzn-Trace-Id")); // prefer Lambda runtime header, then environment, then inbound header - String resolved = runtimeTrace != null ? runtimeTrace - : envTrace != null ? envTrace - : headerTrace; + String resolved = runtimeTrace != null ? runtimeTrace : envTrace != null ? envTrace : headerTrace; if (resolved != null) { System.setProperty("com.amazonaws.xray.traceHeader", resolved); @@ -280,7 +286,8 @@ public String toString() { return context; } - private void propagateAwsError(String requestId, Exception e, JsonMapper mapper, String runtimeApi, RestTemplate rest) { + private void propagateAwsError(String requestId, Exception e, JsonMapper mapper, String runtimeApi, + RestTemplate rest) { String errorMessage = e.getMessage(); String errorType = e.getClass().getSimpleName(); StringWriter sw = new StringWriter(); @@ -293,10 +300,11 @@ private void propagateAwsError(String requestId, Exception e, JsonMapper mapper, em.put("stackTrace", stackTrace); byte[] outputBody = mapper.toJson(em); try { - String errorUrl = MessageFormat.format(LAMBDA_ERROR_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE, requestId); - ResponseEntity result = rest.exchange(RequestEntity.post(URI.create(errorUrl)) - .header(USER_AGENT, USER_AGENT_VALUE) - .body(outputBody), Object.class); + String errorUrl = MessageFormat.format(LAMBDA_ERROR_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE, + requestId); + ResponseEntity result = rest.exchange( + RequestEntity.post(URI.create(errorUrl)).header(USER_AGENT, USER_AGENT_VALUE).body(outputBody), + Object.class); if (logger.isInfoEnabled()) { logger.info("Result ERROR status: " + result.getStatusCode()); } @@ -366,8 +374,8 @@ private FunctionInvocationWrapper locateFunction(Environment environment, Functi this.routingFunction = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME, "application/json"); if (this.routingFunction != null && logger.isInfoEnabled()) { logger.info("Will default to RoutingFunction, since multiple functions available in FunctionCatalog." - + "Expecting 'spring.cloud.function.definition' or 'spring.cloud.function.routing-expression' as Message headers. " - + "If invocation is over API Gateway, Message headers can be provided as HTTP headers."); + + "Expecting 'spring.cloud.function.definition' or 'spring.cloud.function.routing-expression' as Message headers. " + + "If invocation is over API Gateway, Message headers can be provided as HTTP headers."); } function = this.routingFunction; } @@ -399,4 +407,5 @@ private static String extractVersion() { } } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java index 542389300..f0435555c 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java @@ -67,10 +67,9 @@ private boolean isCustomRuntime(Environment environment) { return false; } - private boolean isWebExportEnabled(GenericApplicationContext context) { Boolean enabled = context.getEnvironment() - .getProperty("spring.cloud.function.web.export.enabled", Boolean.class); + .getProperty("spring.cloud.function.web.export.enabled", Boolean.class); return enabled != null && enabled; } 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 801869a19..4663469d5 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 @@ -46,12 +46,11 @@ import org.springframework.util.StringUtils; /** - * * @author Oleg Zhurakousky * @since 3.1 * - * see - * https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format + * see + * https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format */ public class FunctionInvoker implements RequestStreamHandler { @@ -89,21 +88,26 @@ public void handleRequest(InputStream input, OutputStream output, Context contex if (context == null) { logger.warn("Lambda is invoked with null Context"); } - Message requestMessage = AWSLambdaUtils - .generateMessage(input, this.function.getInputType(), this.function.isSupplier(), jsonMapper, context); + Message requestMessage = AWSLambdaUtils.generateMessage(input, this.function.getInputType(), + this.function.isSupplier(), jsonMapper, context); Object response = this.function.apply(requestMessage); - byte[] responseBytes = AWSLambdaUtils.generateOutputFromObject(requestMessage, response, this.jsonMapper, function.getOutputType()); + byte[] responseBytes = AWSLambdaUtils.generateOutputFromObject(requestMessage, response, this.jsonMapper, + function.getOutputType()); StreamUtils.copy(responseBytes, output); // any exception should propagate } private void start() { Class startClass = FunctionClassUtils.getStartClass(); - String[] properties = new String[] {"--spring.cloud.function.web.export.enabled=false", "--spring.main.web-application-type=none"}; + String[] properties = new String[] { "--spring.cloud.function.web.export.enabled=false", + "--spring.main.web-application-type=none" }; ConfigurableApplicationContext context = ApplicationContextInitializer.class.isAssignableFrom(startClass) - ? FunctionalSpringApplication.run(new Class[] {startClass, AWSCompanionAutoConfiguration.class}, properties) - : new SpringApplicationBuilder().main(startClass).sources(new Class[] {startClass, AWSCompanionAutoConfiguration.class}).run(properties); + ? FunctionalSpringApplication.run(new Class[] { startClass, AWSCompanionAutoConfiguration.class }, + properties) + : new SpringApplicationBuilder().main(startClass) + .sources(new Class[] { startClass, AWSCompanionAutoConfiguration.class }) + .run(properties); Environment environment = context.getEnvironment(); if (!StringUtils.hasText(this.functionDefinition)) { @@ -117,7 +121,7 @@ private void start() { if (!objectMapper.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)) { MapperBuilder builder = objectMapper.rebuild(); builder.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); - objectMapper = builder.build(); + objectMapper = builder.build(); } }); } @@ -131,14 +135,16 @@ private void start() { if (this.function == null) { if (logger.isInfoEnabled()) { if (!StringUtils.hasText(this.functionDefinition)) { - logger.info("Failed to determine default function. Please use 'spring.cloud.function.definition' property " - + "or pass function definition as a constructor argument to this FunctionInvoker"); + logger.info( + "Failed to determine default function. Please use 'spring.cloud.function.definition' property " + + "or pass function definition as a constructor argument to this FunctionInvoker"); } Set names = functionCatalog.getNames(null); if (names.size() == 1) { - logger.info("Will default to RoutingFunction, since it is the only function available in FunctionCatalog." - + "Expecting 'spring.cloud.function.definition' or 'spring.cloud.function.routing-expression' as Message headers. " - + "If invocation is over API Gateway, Message headers can be provided as HTTP headers."); + logger + .info("Will default to RoutingFunction, since it is the only function available in FunctionCatalog." + + "Expecting 'spring.cloud.function.definition' or 'spring.cloud.function.routing-expression' as Message headers. " + + "If invocation is over API Gateway, Message headers can be provided as HTTP headers."); } else { logger.info("More than one function is available in FunctionCatalog. " + names @@ -161,4 +167,5 @@ private void start() { } this.started = true; } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/test/aws/AWSCustomRuntime.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/test/aws/AWSCustomRuntime.java index 28e303884..3446efd66 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/test/aws/AWSCustomRuntime.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/test/aws/AWSCustomRuntime.java @@ -38,16 +38,17 @@ * @since 3.2 */ @EnableAutoConfiguration -public class AWSCustomRuntime { +public class AWSCustomRuntime { BlockingQueue inputQueue = new ArrayBlockingQueue<>(3); BlockingQueue> outputQueue = new ArrayBlockingQueue<>(3); public AWSCustomRuntime(ConfigurableApplicationContext context) { - context.getEnvironment().getPropertySources().addFirst( - new MapPropertySource("AWSCustomRuntime", - Map.of("AWS_LAMBDA_RUNTIME_API", "localhost:${local.server.port}"))); + context.getEnvironment() + .getPropertySources() + .addFirst(new MapPropertySource("AWSCustomRuntime", + Map.of("AWS_LAMBDA_RUNTIME_API", "localhost:${local.server.port}"))); } @Bean("2018-06-01/runtime/invocation/consume/response") @@ -67,10 +68,9 @@ Supplier> supply() { } if (!(value instanceof Message)) { return MessageBuilder.withPayload((String) value) - .setHeader("Lambda-Runtime-Aws-Request-Id", "consume") - .setHeader("Content-Type", - MimeTypeUtils.APPLICATION_JSON) - .build(); + .setHeader("Lambda-Runtime-Aws-Request-Id", "consume") + .setHeader("Content-Type", MimeTypeUtils.APPLICATION_JSON) + .build(); } else { return (Message) value; 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 82da66b30..6337330a0 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 @@ -34,95 +34,49 @@ import org.springframework.test.annotation.DirtiesContext; import static org.assertj.core.api.Assertions.assertThat; + /** - * * @author Oleg Zhurakousky */ public class CustomRuntimeEventLoopTest { - private String API_EVENT = "{\n" - + " \"version\": \"1.0\",\n" - + " \"resource\": \"$default\",\n" - + " \"path\": \"/question\",\n" - + " \"httpMethod\": \"POST\",\n" - + " \"headers\": {\n" - + " \"Content-Length\": \"40\",\n" - + " \"Content-Type\": \"application/json\",\n" + private String API_EVENT = "{\n" + " \"version\": \"1.0\",\n" + " \"resource\": \"$default\",\n" + + " \"path\": \"/question\",\n" + " \"httpMethod\": \"POST\",\n" + " \"headers\": {\n" + + " \"Content-Length\": \"40\",\n" + " \"Content-Type\": \"application/json\",\n" + " \"Host\": \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\",\n" + " \"User-Agent\": \"curl/7.88.1\",\n" + " \"X-Amzn-Trace-Id\": \"Root=1-64ad9787-4c89d5af7607eb9e522e01d5\",\n" - + " \"X-Forwarded-For\": \"109.210.252.44\",\n" - + " \"X-Forwarded-Port\": \"443\",\n" - + " \"X-Forwarded-Proto\": \"https\",\n" - + " \"accept\": \"*/*\"\n" - + " },\n" - + " \"multiValueHeaders\": {\n" - + " \"Content-Length\": [\n" - + " \"40\"\n" - + " ],\n" - + " \"Content-Type\": [\n" - + " \"application/json\"\n" - + " ],\n" - + " \"Host\": [\n" - + " \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\"\n" - + " ],\n" - + " \"User-Agent\": [\n" - + " \"curl/7.88.1\"\n" - + " ],\n" - + " \"X-Amzn-Trace-Id\": [\n" - + " \"Root=1-64ad9787-4c89d5af7607eb9e522e01d5\"\n" - + " ],\n" - + " \"X-Forwarded-For\": [\n" - + " \"109.210.252.44\"\n" - + " ],\n" - + " \"X-Forwarded-Port\": [\n" - + " \"443\"\n" - + " ],\n" - + " \"X-Forwarded-Proto\": [\n" - + " \"https\"\n" - + " ],\n" - + " \"accept\": [\n" - + " \"*/*\"\n" - + " ]\n" - + " },\n" - + " \"queryStringParameters\": null,\n" - + " \"multiValueQueryStringParameters\": null,\n" - + " \"requestContext\": {\n" - + " \"accountId\": \"313369169943\",\n" + + " \"X-Forwarded-For\": \"109.210.252.44\",\n" + " \"X-Forwarded-Port\": \"443\",\n" + + " \"X-Forwarded-Proto\": \"https\",\n" + " \"accept\": \"*/*\"\n" + " },\n" + + " \"multiValueHeaders\": {\n" + " \"Content-Length\": [\n" + " \"40\"\n" + + " ],\n" + " \"Content-Type\": [\n" + " \"application/json\"\n" + " ],\n" + + " \"Host\": [\n" + " \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\"\n" + + " ],\n" + " \"User-Agent\": [\n" + " \"curl/7.88.1\"\n" + " ],\n" + + " \"X-Amzn-Trace-Id\": [\n" + " \"Root=1-64ad9787-4c89d5af7607eb9e522e01d5\"\n" + + " ],\n" + " \"X-Forwarded-For\": [\n" + " \"109.210.252.44\"\n" + " ],\n" + + " \"X-Forwarded-Port\": [\n" + " \"443\"\n" + " ],\n" + + " \"X-Forwarded-Proto\": [\n" + " \"https\"\n" + " ],\n" + + " \"accept\": [\n" + " \"*/*\"\n" + " ]\n" + " },\n" + + " \"queryStringParameters\": null,\n" + " \"multiValueQueryStringParameters\": null,\n" + + " \"requestContext\": {\n" + " \"accountId\": \"313369169943\",\n" + " \"apiId\": \"emcdxu5ijj\",\n" + " \"domainName\": \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\",\n" - + " \"domainPrefix\": \"emcdxu5ijj\",\n" - + " \"extendedRequestId\": \"H6SdPgXtiYcEP1w=\",\n" - + " \"httpMethod\": \"POST\",\n" - + " \"identity\": {\n" - + " \"accessKey\": null,\n" - + " \"accountId\": null,\n" - + " \"caller\": null,\n" - + " \"cognitoAmr\": null,\n" - + " \"cognitoAuthenticationProvider\": null,\n" - + " \"cognitoAuthenticationType\": null,\n" - + " \"cognitoIdentityId\": null,\n" - + " \"cognitoIdentityPoolId\": null,\n" - + " \"principalOrgId\": null,\n" - + " \"sourceIp\": \"109.210.252.44\",\n" - + " \"user\": null,\n" - + " \"userAgent\": \"curl/7.88.1\",\n" - + " \"userArn\": null\n" - + " },\n" - + " \"path\": \"/question\",\n" - + " \"protocol\": \"HTTP/1.1\",\n" + + " \"domainPrefix\": \"emcdxu5ijj\",\n" + " \"extendedRequestId\": \"H6SdPgXtiYcEP1w=\",\n" + + " \"httpMethod\": \"POST\",\n" + " \"identity\": {\n" + " \"accessKey\": null,\n" + + " \"accountId\": null,\n" + " \"caller\": null,\n" + + " \"cognitoAmr\": null,\n" + " \"cognitoAuthenticationProvider\": null,\n" + + " \"cognitoAuthenticationType\": null,\n" + " \"cognitoIdentityId\": null,\n" + + " \"cognitoIdentityPoolId\": null,\n" + " \"principalOrgId\": null,\n" + + " \"sourceIp\": \"109.210.252.44\",\n" + " \"user\": null,\n" + + " \"userAgent\": \"curl/7.88.1\",\n" + " \"userArn\": null\n" + " },\n" + + " \"path\": \"/question\",\n" + " \"protocol\": \"HTTP/1.1\",\n" + " \"requestId\": \"H6SdPgXtiYcEP1w=\",\n" + " \"requestTime\": \"11/Jul/2023:17:55:19 +0000\",\n" - + " \"requestTimeEpoch\": 1689098119662,\n" - + " \"resourceId\": \"$default\",\n" - + " \"resourcePath\": \"$default\",\n" - + " \"stage\": \"$default\"\n" - + " },\n" - + " \"pathParameters\": null,\n" - + " \"stageVariables\": null,\n" + + " \"requestTimeEpoch\": 1689098119662,\n" + " \"resourceId\": \"$default\",\n" + + " \"resourcePath\": \"$default\",\n" + " \"stage\": \"$default\"\n" + " },\n" + + " \"pathParameters\": null,\n" + " \"stageVariables\": null,\n" + " \"body\": \"[{\\\"latitude\\\": 41.34, \\\"longitude\\\": 2.78},{\\\"latitude\\\": 43.24, \\\"longitude\\\": 3.78}]\",\n" - + " \"isBase64Encoded\": false\n" - + "}"; + + " \"isBase64Encoded\": false\n" + "}"; @Test public void testDefaultFunctionLookup() throws Exception { @@ -135,11 +89,10 @@ public void testDefaultFunctionLookupReactive() throws Exception { } private void testDefaultFunctionLookup(String handler, Class context) throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(context, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=" + handler, "server.port=0") - .run()) { + try (ConfigurableApplicationContext userContext = new SpringApplicationBuilder(context, AWSCustomRuntime.class) + .web(WebApplicationType.SERVLET) + .properties("_HANDLER=" + handler, "server.port=0") + .run()) { AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); Message replyMessage = aws.exchange("\"ricky\""); @@ -151,13 +104,13 @@ private void testDefaultFunctionLookup(String handler, Class context) throws } } -// @Test + // @Test public void testDefaultFunctionAsComponentLookup() throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(PersonFunction.class, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=personFunction", "server.port=0") - .run()) { + try (ConfigurableApplicationContext userContext = new SpringApplicationBuilder(PersonFunction.class, + AWSCustomRuntime.class) + .web(WebApplicationType.SERVLET) + .properties("_HANDLER=personFunction", "server.port=0") + .run()) { AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); @@ -167,13 +120,13 @@ public void testDefaultFunctionAsComponentLookup() throws Exception { } } -// @Test + // @Test public void test_HANDLERlookupAndPojoFunction() throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(MultipleFunctionConfiguration.class, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=uppercasePerson", "server.port=0") - .run()) { + try (ConfigurableApplicationContext userContext = new SpringApplicationBuilder( + MultipleFunctionConfiguration.class, AWSCustomRuntime.class) + .web(WebApplicationType.SERVLET) + .properties("_HANDLER=uppercasePerson", "server.port=0") + .run()) { AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); @@ -186,11 +139,11 @@ public void test_HANDLERlookupAndPojoFunction() throws Exception { @Test public void test_HANDLERWithApiGatewayRequestAndFlux() throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(MultipleFunctionConfiguration.class, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=echoFlux", "server.port=0") - .run()) { + try (ConfigurableApplicationContext userContext = new SpringApplicationBuilder( + MultipleFunctionConfiguration.class, AWSCustomRuntime.class) + .web(WebApplicationType.SERVLET) + .properties("_HANDLER=echoFlux", "server.port=0") + .run()) { AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); String response = aws.exchange(API_EVENT).getPayload(); @@ -202,11 +155,11 @@ public void test_HANDLERWithApiGatewayRequestAndFlux() throws Exception { @Test @DirtiesContext public void test_definitionLookupAndComposition() throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(MultipleFunctionConfiguration.class, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=toPersonJson|uppercasePerson", "server.port=0") - .run()) { + try (ConfigurableApplicationContext userContext = new SpringApplicationBuilder( + MultipleFunctionConfiguration.class, AWSCustomRuntime.class) + .web(WebApplicationType.SERVLET) + .properties("_HANDLER=toPersonJson|uppercasePerson", "server.port=0") + .run()) { AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); @@ -219,23 +172,28 @@ public void test_definitionLookupAndComposition() throws Exception { @EnableAutoConfiguration protected static class SingleFunctionConfiguration { + @Bean public Function uppercase() { return v -> v.toUpperCase(Locale.ROOT); } + } @EnableAutoConfiguration protected static class SingleFunctionConfigurationReactive { + @Bean public Function, Flux> uppercase() { return v -> v.map(String::toUpperCase); } + } @EnableAutoConfiguration @Configuration(proxyBeanMethods = false) protected static class MultipleFunctionConfiguration { + @Bean public Function uppercase() { return v -> v.toUpperCase(Locale.ROOT); @@ -257,10 +215,13 @@ public Function, Flux> echoFlux() { return new GeoLocation(g.longitude(), g.latitude()); }); } + } @EnableAutoConfiguration - @Component("personFunction") // need in test explicitly since it is inner class and name wil be `customRuntimeEventLoopTest.PersonFunction` + @Component("personFunction") // need in test explicitly since it is inner class and + // name wil be + // `customRuntimeEventLoopTest.PersonFunction` public static class PersonFunction implements Function { public PersonFunction() { @@ -271,9 +232,11 @@ public PersonFunction() { public Person apply(Person input) { return new Person(input.getName().toUpperCase(Locale.ROOT)); } + } public static class Person { + private String name; public Person() { @@ -291,9 +254,10 @@ public String getName() { public void setName(String name) { this.name = name; } - } + } public record GeoLocation(Float latitude, Float longitude) { } + } 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 3516a7c0f..519b30dd2 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 @@ -68,7 +68,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * */ @@ -80,691 +79,308 @@ public class FunctionInvokerTests { String jsonPojoCollection = "[{\"name\":\"Ricky\"},{\"name\":\"Julien\"},{\"name\":\"Julien\"}]"; - String someEvent = "{\n" - + " \"payload\": {\n" - + " \"headers\": {\n" - + " \"businessUnit\": \"1\"\n" - + " }\n" - + " },\n" - + " \"headers\": {\n" - + " \"aws-context\": {\n" - + " \"memoryLimit\": 1024,\n" + String someEvent = "{\n" + " \"payload\": {\n" + " \"headers\": {\n" + + " \"businessUnit\": \"1\"\n" + " }\n" + " },\n" + " \"headers\": {\n" + + " \"aws-context\": {\n" + " \"memoryLimit\": 1024,\n" + " \"awsRequestId\": \"87a211bf-540f-4f9f-a218-d096a0099999\",\n" - + " \"functionName\": \"myfunction\",\n" - + " \"functionVersion\": \"278\",\n" + + " \"functionName\": \"myfunction\",\n" + " \"functionVersion\": \"278\",\n" + " \"invokedFunctionArn\": \"arn:aws:lambda:us-east-1:xxxxxxx:function:xxxxx:snapstart\",\n" - + " \"deadlineTimeInMs\": 1712717704761,\n" - + " \"logger\": {\n" - + " \"logFiltering\": {\n" - + " \"minimumLogLevel\": \"UNDEFINED\"\n" - + " },\n" - + " \"logFormatter\": {},\n" - + " \"logFormat\": \"TEXT\"\n" - + " }\n" - + " },\n" - + " \"businessUnit\": \"1\",\n" - + " \"id\": \"xxxx\",\n" - + " \"aws-event\": true,\n" - + " \"timestamp\": 1712716805129\n" - + " }\n" - + "}"; - - String scheduleEvent = "{\n" - + " \"version\": \"0\",\n" - + " \"id\": \"17793124-05d4-b198-2fde-7ededc63b103\",\n" - + " \"detail-type\": \"Object Created\",\n" - + " \"source\": \"aws.s3\",\n" - + " \"account\": \"111122223333\",\n" - + " \"time\": \"2021-11-12T00:00:00Z\",\n" - + " \"region\": \"ca-central-1\",\n" - + " \"resources\": [\n" - + " \"arn:aws:s3:::amzn-s3-demo-bucket1\"\n" - + " ],\n" - + " \"detail\": {\n" - + " \"version\": \"0\",\n" - + " \"bucket\": {\n" - + " \"name\": \"amzn-s3-demo-bucket1\"\n" - + " },\n" - + " \"object\": {\n" - + " \"key\": \"example-key\",\n" - + " \"size\": 5,\n" + + " \"deadlineTimeInMs\": 1712717704761,\n" + " \"logger\": {\n" + + " \"logFiltering\": {\n" + " \"minimumLogLevel\": \"UNDEFINED\"\n" + + " },\n" + " \"logFormatter\": {},\n" + + " \"logFormat\": \"TEXT\"\n" + " }\n" + " },\n" + + " \"businessUnit\": \"1\",\n" + " \"id\": \"xxxx\",\n" + " \"aws-event\": true,\n" + + " \"timestamp\": 1712716805129\n" + " }\n" + "}"; + + String scheduleEvent = "{\n" + " \"version\": \"0\",\n" + " \"id\": \"17793124-05d4-b198-2fde-7ededc63b103\",\n" + + " \"detail-type\": \"Object Created\",\n" + " \"source\": \"aws.s3\",\n" + + " \"account\": \"111122223333\",\n" + " \"time\": \"2021-11-12T00:00:00Z\",\n" + + " \"region\": \"ca-central-1\",\n" + " \"resources\": [\n" + + " \"arn:aws:s3:::amzn-s3-demo-bucket1\"\n" + " ],\n" + " \"detail\": {\n" + + " \"version\": \"0\",\n" + " \"bucket\": {\n" + " \"name\": \"amzn-s3-demo-bucket1\"\n" + + " },\n" + " \"object\": {\n" + " \"key\": \"example-key\",\n" + " \"size\": 5,\n" + " \"etag\": \"b1946ac92492d2347c6235b4d2611184\",\n" + " \"version-id\": \"IYV3p45BT0ac8hjHg1houSdS1a.Mro8e\",\n" - + " \"sequencer\": \"617f08299329d189\"\n" - + " },\n" - + " \"request-id\": \"N4N7GDK58NMKJ12R\",\n" - + " \"requester\": \"123456789012\",\n" - + " \"source-ip-address\": \"1.2.3.4\",\n" - + " \"reason\": \"PutObject\"\n" - + " }\n" - + "} "; - - String dynamoDbEvent = "{\n" - + " \"Records\": [\n" - + " {\n" - + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e69f274ee\",\n" - + " \"eventName\": \"INSERT\",\n" - + " \"eventVersion\": \"1.1\",\n" - + " \"eventSource\": \"aws:dynamodb\",\n" - + " \"awsRegion\": \"us-east-1\",\n" - + " \"userIdentity\":{\n" - + " \"type\":\"Service\",\n" - + " \"principalId\":\"dynamodb.amazonaws.com\"\n" - + " },\n" - + " \"dynamodb\": {\n" - + " \"ApproximateCreationDateTime\": 1.684934517E9,\n" - + " \"Keys\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " }\n" - + " },\n" - + " \"NewImage\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"asdf1\": {\n" - + " \"B\": \"AAEqQQ==\"\n" - + " },\n" - + " \"asdf2\": {\n" - + " \"BS\": [\n" - + " \"AAEqQQ==\",\n" - + " \"QSoBAA==\"\n" - + " ]\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " }\n" - + " },\n" - + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" - + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" - + " },\n" + + " \"sequencer\": \"617f08299329d189\"\n" + " },\n" + " \"request-id\": \"N4N7GDK58NMKJ12R\",\n" + + " \"requester\": \"123456789012\",\n" + " \"source-ip-address\": \"1.2.3.4\",\n" + + " \"reason\": \"PutObject\"\n" + " }\n" + "} "; + + String dynamoDbEvent = "{\n" + " \"Records\": [\n" + " {\n" + + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e69f274ee\",\n" + " \"eventName\": \"INSERT\",\n" + + " \"eventVersion\": \"1.1\",\n" + " \"eventSource\": \"aws:dynamodb\",\n" + + " \"awsRegion\": \"us-east-1\",\n" + " \"userIdentity\":{\n" + " \"type\":\"Service\",\n" + + " \"principalId\":\"dynamodb.amazonaws.com\"\n" + " },\n" + " \"dynamodb\": {\n" + + " \"ApproximateCreationDateTime\": 1.684934517E9,\n" + " \"Keys\": {\n" + + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + " \"key\": {\n" + + " \"S\": \"binary\"\n" + " }\n" + " },\n" + " \"NewImage\": {\n" + + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + " \"asdf1\": {\n" + + " \"B\": \"AAEqQQ==\"\n" + " },\n" + " \"asdf2\": {\n" + + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"QSoBAA==\"\n" + + " ]\n" + " },\n" + " \"key\": {\n" + " \"S\": \"binary\"\n" + + " }\n" + " },\n" + " \"SequenceNumber\": \"1405400000000002063282832\",\n" + + " \"SizeBytes\": 54,\n" + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" + " },\n" + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " },\n" - + " {\n" - + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e42f274ee\",\n" - + " \"eventName\": \"INSERT\",\n" - + " \"eventVersion\": \"1.1\",\n" - + " \"eventSource\": \"aws:dynamodb\",\n" - + " \"awsRegion\": \"us-east-1\",\n" - + " \"dynamodb\": {\n" - + " \"ApproximateCreationDateTime\": 1480642020,\n" - + " \"Keys\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " }\n" - + " },\n" - + " \"NewImage\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"asdf1\": {\n" - + " \"B\": \"AAEqQQ==\"\n" - + " },\n" - + " \"b2\": {\n" - + " \"B\": \"test\"\n" - + " },\n" - + " \"asdf2\": {\n" - + " \"BS\": [\n" - + " \"AAEqQQ==\",\n" - + " \"QSoBAA==\",\n" - + " \"AAEqQQ==\"\n" - + " ]\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " },\n" - + " \"Binary\": {\n" - + " \"B\": \"AAEqQQ==\"\n" - + " },\n" - + " \"Boolean\": {\n" - + " \"BOOL\": true\n" - + " },\n" - + " \"BinarySet\": {\n" - + " \"BS\": [\n" - + " \"AAEqQQ==\",\n" - + " \"AAEqQQ==\"\n" - + " ]\n" - + " },\n" - + " \"List\": {\n" - + " \"L\": [\n" - + " {\n" - + " \"S\": \"Cookies\"\n" - + " },\n" - + " {\n" - + " \"S\": \"Coffee\"\n" - + " },\n" - + " {\n" - + " \"N\": \"3.14159\"\n" - + " }\n" - + " ]\n" - + " },\n" - + " \"Map\": {\n" - + " \"M\": {\n" - + " \"Name\": {\n" - + " \"S\": \"Joe\"\n" - + " },\n" - + " \"Age\": {\n" - + " \"N\": \"35\"\n" - + " }\n" - + " }\n" - + " },\n" - + " \"FloatNumber\": {\n" - + " \"N\": \"123.45\"\n" - + " },\n" - + " \"IntegerNumber\": {\n" - + " \"N\": \"123\"\n" - + " },\n" - + " \"NumberSet\": {\n" - + " \"NS\": [\n" - + " \"1234\",\n" - + " \"567.8\"\n" - + " ]\n" - + " },\n" - + " \"Null\": {\n" - + " \"NULL\": true\n" - + " },\n" - + " \"String\": {\n" - + " \"S\": \"Hello\"\n" - + " },\n" - + " \"StringSet\": {\n" - + " \"SS\": [\n" - + " \"Giraffe\",\n" - + " \"Zebra\"\n" - + " ]\n" - + " },\n" - + " \"EmptyStringSet\": {\n" - + " \"SS\": []\n" - + " }\n" - + " },\n" - + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" - + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" - + " },\n" + + " },\n" + " {\n" + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e42f274ee\",\n" + + " \"eventName\": \"INSERT\",\n" + " \"eventVersion\": \"1.1\",\n" + + " \"eventSource\": \"aws:dynamodb\",\n" + " \"awsRegion\": \"us-east-1\",\n" + + " \"dynamodb\": {\n" + " \"ApproximateCreationDateTime\": 1480642020,\n" + + " \"Keys\": {\n" + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + + " \"key\": {\n" + " \"S\": \"binary\"\n" + " }\n" + " },\n" + + " \"NewImage\": {\n" + " \"val\": {\n" + " \"S\": \"data\"\n" + + " },\n" + " \"asdf1\": {\n" + " \"B\": \"AAEqQQ==\"\n" + " },\n" + + " \"b2\": {\n" + " \"B\": \"test\"\n" + " },\n" + " \"asdf2\": {\n" + + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"QSoBAA==\",\n" + + " \"AAEqQQ==\"\n" + " ]\n" + " },\n" + " \"key\": {\n" + + " \"S\": \"binary\"\n" + " },\n" + " \"Binary\": {\n" + + " \"B\": \"AAEqQQ==\"\n" + " },\n" + " \"Boolean\": {\n" + + " \"BOOL\": true\n" + " },\n" + " \"BinarySet\": {\n" + + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"AAEqQQ==\"\n" + + " ]\n" + " },\n" + " \"List\": {\n" + " \"L\": [\n" + + " {\n" + " \"S\": \"Cookies\"\n" + " },\n" + " {\n" + + " \"S\": \"Coffee\"\n" + " },\n" + " {\n" + + " \"N\": \"3.14159\"\n" + " }\n" + " ]\n" + " },\n" + + " \"Map\": {\n" + " \"M\": {\n" + " \"Name\": {\n" + + " \"S\": \"Joe\"\n" + " },\n" + " \"Age\": {\n" + + " \"N\": \"35\"\n" + " }\n" + " }\n" + " },\n" + + " \"FloatNumber\": {\n" + " \"N\": \"123.45\"\n" + " },\n" + + " \"IntegerNumber\": {\n" + " \"N\": \"123\"\n" + " },\n" + + " \"NumberSet\": {\n" + " \"NS\": [\n" + " \"1234\",\n" + + " \"567.8\"\n" + " ]\n" + " },\n" + " \"Null\": {\n" + + " \"NULL\": true\n" + " },\n" + " \"String\": {\n" + + " \"S\": \"Hello\"\n" + " },\n" + " \"StringSet\": {\n" + + " \"SS\": [\n" + " \"Giraffe\",\n" + " \"Zebra\"\n" + + " ]\n" + " },\n" + " \"EmptyStringSet\": {\n" + " \"SS\": []\n" + + " }\n" + " },\n" + " \"SequenceNumber\": \"1405400000000002063282832\",\n" + + " \"SizeBytes\": 54,\n" + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" + " },\n" + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " }\n" - + " ]\n" - + "}"; + + " }\n" + " ]\n" + "}"; - String sampleLBEvent = "{\n" - + " \"requestContext\": {\n" - + " \"elb\": {\n" + String sampleLBEvent = "{\n" + " \"requestContext\": {\n" + " \"elb\": {\n" + " \"targetGroupArn\": \"arn:aws:elasticloadbalancing:us-east-1:XXXXXXXXXXX:targetgroup/sample/6d0ecf831eec9f09\"\n" - + " }\n" - + " },\n" - + " \"httpMethod\": \"GET\",\n" - + " \"path\": \"/\",\n" - + " \"queryStringParameters\": {},\n" - + " \"headers\": {\n" + + " }\n" + " },\n" + " \"httpMethod\": \"GET\",\n" + " \"path\": \"/\",\n" + + " \"queryStringParameters\": {},\n" + " \"headers\": {\n" + " \"accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n" - + " \"accept-encoding\": \"gzip\",\n" - + " \"accept-language\": \"en-US,en;q=0.5\",\n" - + " \"connection\": \"keep-alive\",\n" - + " \"cookie\": \"name=value\",\n" - + " \"host\": \"lambda-YYYYYYYY.elb.amazonaws.com\",\n" - + " \"upgrade-insecure-requests\": \"1\",\n" + + " \"accept-encoding\": \"gzip\",\n" + " \"accept-language\": \"en-US,en;q=0.5\",\n" + + " \"connection\": \"keep-alive\",\n" + " \"cookie\": \"name=value\",\n" + + " \"host\": \"lambda-YYYYYYYY.elb.amazonaws.com\",\n" + " \"upgrade-insecure-requests\": \"1\",\n" + " \"user-agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:60.0) Gecko/20100101 Firefox/60.0\",\n" + " \"x-amzn-trace-id\": \"Root=1-5bdb40ca-556d8b0c50dc66f0511bf520\",\n" - + " \"x-forwarded-for\": \"192.0.2.1\",\n" - + " \"x-forwarded-port\": \"80\",\n" - + " \"x-forwarded-proto\": \"http\"\n" - + " },\n" - + " \"body\": \"Hello from ELB\",\n" - + " \"isBase64Encoded\": false\n" - + "}"; - - String sampleSQSEvent = "{\n" + - " \"Records\": [\n" + - " {\n" + - " \"messageId\": \"19dd0b57-b21e-4ac1-bd88-01bbb068cb78\",\n" + - " \"receiptHandle\": \"MessageReceiptHandle\",\n" + - " \"body\": \"Hello from SQS!\",\n" + - " \"attributes\": {\n" + - " \"ApproximateReceiveCount\": \"1\",\n" + - " \"SentTimestamp\": \"1523232000000\",\n" + - " \"SenderId\": \"123456789012\",\n" + - " \"ApproximateFirstReceiveTimestamp\": \"1523232000001\"\n" + - " },\n" + - " \"messageAttributes\": {},\n" + - " \"md5OfBody\": \"7b270e59b47ff90a553787216d55d91d\",\n" + - " \"eventSource\": \"aws:sqs\",\n" + - " \"eventSourceARN\": \"arn:aws:sqs:eu-central-1:123456789012:MyQueue\",\n" + - " \"awsRegion\": \"eu-central-1\"\n" + - " }\n" + - " ]\n" + - "}"; - - String sampleSNSEvent = "{\n" + - " \"Records\": [\n" + - " {\n" + - " \"EventVersion\": \"1.0\",\n" + - " \"EventSubscriptionArn\": \"arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + - " \"EventSource\": \"aws:sns\",\n" + - " \"Sns\": {\n" + - " \"SignatureVersion\": \"1\",\n" + - " \"Timestamp\": \"2019-01-02T12:45:07.000Z\",\n" + - " \"Signature\": \"tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==\",\n" + - " \"SigningCertUrl\": \"https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem\",\n" + - " \"MessageId\": \"95df01b4-ee98-5cb9-9903-4c221d41eb5e\",\n" + - " \"Message\": \"Hello from SNS!\",\n" + - " \"MessageAttributes\": {\n" + - " \"Test\": {\n" + - " \"Type\": \"String\",\n" + - " \"Value\": \"TestString\"\n" + - " },\n" + - " \"TestBinary\": {\n" + - " \"Type\": \"Binary\",\n" + - " \"Value\": \"TestBinary\"\n" + - " }\n" + - " },\n" + - " \"Type\": \"Notification\",\n" + - " \"UnsubscribeUrl\": \"https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + - " \"TopicArn\":\"arn:aws:sns:us-east-2:123456789012:sns-lambda\",\n" + - " \"Subject\": \"TestInvoke\"\n" + - " }\n" + - " }\n" + - " ]\n" + - "}"; - - String sampleKinesisEvent = "{" + - " \"Records\": [" + - " {" + - " \"kinesis\": {" + - " \"kinesisSchemaVersion\": \"1.0\"," + - " \"partitionKey\": \"1\"," + - " \"sequenceNumber\": \"49590338271490256608559692538361571095921575989136588898\"," + - " \"data\": \"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==\"," + - " \"approximateArrivalTimestamp\": 1545084650.987" + - " }," + - " \"eventSource\": \"aws:kinesis\"," + - " \"eventVersion\": \"1.0\"," + - " \"eventID\": \"shardId-000000000006:49590338271490256608559692538361571095921575989136588898\"," + - " \"eventName\": \"aws:kinesis:record\"," + - " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\"," + - " \"awsRegion\": \"us-east-2\"," + - " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"" + - " }," + - " {" + - " \"kinesis\": {" + - " \"kinesisSchemaVersion\": \"1.0\"," + - " \"partitionKey\": \"1\"," + - " \"sequenceNumber\": \"49590338271490256608559692540925702759324208523137515618\"," + - " \"data\": \"VGhpcyBpcyBvbmx5IGEgdGVzdC4=\"," + - " \"approximateArrivalTimestamp\": 1545084711.166" + - " }," + - " \"eventSource\": \"aws:kinesis\"," + - " \"eventVersion\": \"1.0\"," + - " \"eventID\": \"shardId-000000000006:49590338271490256608559692540925702759324208523137515618\"," + - " \"eventName\": \"aws:kinesis:record\"," + - " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\"," + - " \"awsRegion\": \"us-east-2\"," + - " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"" + - " }" + - " ]" + - "}"; - - //https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html - String apiGatewayV2Event = "{\n" + - " \"version\": \"2.0\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"rawPath\": \"/my/path\",\n" + - " \"rawQueryString\": \"parameter1=value1¶meter1=value2¶meter2=value\",\n" + - " \"cookies\": [\n" + - " \"cookie1\",\n" + - " \"cookie2\"\n" + - " ],\n" + - " \"headers\": {\n" + - " \"header1\": \"value1\",\n" + - " \"header2\": \"value1,value2\"\n" + - " },\n" + - " \"queryStringParameters\": {\n" + - " \"parameter1\": \"value1,value2\",\n" + - " \"parameter2\": \"value\"\n" + - " },\n" + - " \"requestContext\": {\n" + - " \"accountId\": \"123456789012\",\n" + - " \"apiId\": \"api-id\",\n" + - " \"authentication\": {\n" + - " \"clientCert\": {\n" + - " \"clientCertPem\": \"CERT_CONTENT\",\n" + - " \"subjectDN\": \"www.example.com\",\n" + - " \"issuerDN\": \"Example issuer\",\n" + - " \"serialNumber\": \"a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1\",\n" + - " \"validity\": {\n" + - " \"notBefore\": \"May 28 12:30:02 2019 GMT\",\n" + - " \"notAfter\": \"Aug 5 09:36:04 2021 GMT\"\n" + - " }\n" + - " }\n" + - " },\n" + - " \"authorizer\": {\n" + - " \"jwt\": {\n" + - " \"claims\": {\n" + - " \"claim1\": \"value1\",\n" + - " \"claim2\": \"value2\"\n" + - " },\n" + - " \"scopes\": [\n" + - " \"scope1\",\n" + - " \"scope2\"\n" + - " ]\n" + - " }\n" + - " },\n" + - " \"domainName\": \"id.execute-api.us-east-1.amazonaws.com\",\n" + - " \"domainPrefix\": \"id\",\n" + - " \"http\": {\n" + - " \"method\": \"POST\",\n" + - " \"path\": \"/my/path\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"sourceIp\": \"IP\",\n" + - " \"userAgent\": \"agent\"\n" + - " },\n" + - " \"requestId\": \"id\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"stage\": \"$default\",\n" + - " \"time\": \"12/Mar/2020:19:03:58 +0000\",\n" + - " \"timeEpoch\": 1583348638390\n" + - " },\n" + - " \"body\": \"Hello from Lambda\",\n" + - " \"pathParameters\": {\n" + - " \"parameter1\": \"value1\"\n" + - " },\n" + - " \"isBase64Encoded\": false,\n" + - " \"stageVariables\": {\n" + - " \"stageVariable1\": \"value1\",\n" + - " \"stageVariable2\": \"value2\"\n" + - " }\n" + - "}"; - - String apiGatewayEvent = "{\n" + - " \"resource\": \"/uppercase2\",\n" + - " \"path\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"headers\": {\n" + - " \"accept\": \"*/*\",\n" + - " \"content-type\": \"application/json\",\n" + - " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"User-Agent\": \"curl/7.54.0\",\n" + - " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" + - " \"X-Forwarded-For\": \"90.37.8.133\",\n" + - " \"X-Forwarded-Port\": \"443\",\n" + - " \"X-Forwarded-Proto\": \"https\"\n" + - " },\n" + - " \"multiValueHeaders\": {\n" + - " \"accept\": [\n" + - " \"*/*\"\n" + - " ],\n" + - " \"content-type\": [\n" + - " \"application/json\"\n" + - " ],\n" + - " \"Host\": [\n" + - " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + - " ],\n" + - " \"User-Agent\": [\n" + - " \"curl/7.54.0\"\n" + - " ],\n" + - " \"X-Amzn-Trace-Id\": [\n" + - " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" + - " ],\n" + - " \"X-Forwarded-For\": [\n" + - " \"90.37.8.133\"\n" + - " ],\n" + - " \"X-Forwarded-Port\": [\n" + - " \"443\"\n" + - " ],\n" + - " \"X-Forwarded-Proto\": [\n" + - " \"https\"\n" + - " ]\n" + - " },\n" + - " \"queryStringParameters\": null,\n" + - " \"multiValueQueryStringParameters\": null,\n" + - " \"pathParameters\": null,\n" + - " \"stageVariables\": null,\n" + - " \"requestContext\": {\n" + - " \"resourceId\": \"qf0io6\",\n" + - " \"resourcePath\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" + - " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + - " \"path\": \"/test/uppercase2\",\n" + - " \"accountId\": \"123456789098\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"stage\": \"test\",\n" + - " \"domainPrefix\": \"fhul32ccy2\",\n" + - " \"requestTimeEpoch\": 1590571934872,\n" + - " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + - " \"identity\": {\n" + - " \"cognitoIdentityPoolId\": null,\n" + - " \"accountId\": null,\n" + - " \"cognitoIdentityId\": null,\n" + - " \"caller\": null,\n" + - " \"sourceIp\": \"90.37.8.133\",\n" + - " \"principalOrgId\": null,\n" + - " \"accessKey\": null,\n" + - " \"cognitoAuthenticationType\": null,\n" + - " \"cognitoAuthenticationProvider\": null,\n" + - " \"userArn\": null,\n" + - " \"userAgent\": \"curl/7.54.0\",\n" + - " \"user\": null\n" + - " },\n" + - " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"apiId\": \"fhul32ccy2\"\n" + - " },\n" + - " \"body\":\"hello\",\n" + - " \"isBase64Encoded\": false\n" + - "}"; - - String s3Event = "{\n" - + " \"Records\": [\n" - + " {\n" - + " \"eventVersion\": \"2.1\",\n" - + " \"eventSource\": \"aws:s3\",\n" - + " \"awsRegion\": \"eu-central-1\",\n" + + " \"x-forwarded-for\": \"192.0.2.1\",\n" + " \"x-forwarded-port\": \"80\",\n" + + " \"x-forwarded-proto\": \"http\"\n" + " },\n" + " \"body\": \"Hello from ELB\",\n" + + " \"isBase64Encoded\": false\n" + "}"; + + String sampleSQSEvent = "{\n" + " \"Records\": [\n" + " {\n" + + " \"messageId\": \"19dd0b57-b21e-4ac1-bd88-01bbb068cb78\",\n" + + " \"receiptHandle\": \"MessageReceiptHandle\",\n" + " \"body\": \"Hello from SQS!\",\n" + + " \"attributes\": {\n" + " \"ApproximateReceiveCount\": \"1\",\n" + + " \"SentTimestamp\": \"1523232000000\",\n" + " \"SenderId\": \"123456789012\",\n" + + " \"ApproximateFirstReceiveTimestamp\": \"1523232000001\"\n" + " },\n" + + " \"messageAttributes\": {},\n" + " \"md5OfBody\": \"7b270e59b47ff90a553787216d55d91d\",\n" + + " \"eventSource\": \"aws:sqs\",\n" + + " \"eventSourceARN\": \"arn:aws:sqs:eu-central-1:123456789012:MyQueue\",\n" + + " \"awsRegion\": \"eu-central-1\"\n" + " }\n" + " ]\n" + "}"; + + String sampleSNSEvent = "{\n" + " \"Records\": [\n" + " {\n" + " \"EventVersion\": \"1.0\",\n" + + " \"EventSubscriptionArn\": \"arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + + " \"EventSource\": \"aws:sns\",\n" + " \"Sns\": {\n" + " \"SignatureVersion\": \"1\",\n" + + " \"Timestamp\": \"2019-01-02T12:45:07.000Z\",\n" + + " \"Signature\": \"tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==\",\n" + + " \"SigningCertUrl\": \"https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem\",\n" + + " \"MessageId\": \"95df01b4-ee98-5cb9-9903-4c221d41eb5e\",\n" + + " \"Message\": \"Hello from SNS!\",\n" + " \"MessageAttributes\": {\n" + + " \"Test\": {\n" + " \"Type\": \"String\",\n" + + " \"Value\": \"TestString\"\n" + " },\n" + " \"TestBinary\": {\n" + + " \"Type\": \"Binary\",\n" + " \"Value\": \"TestBinary\"\n" + " }\n" + + " },\n" + " \"Type\": \"Notification\",\n" + + " \"UnsubscribeUrl\": \"https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + + " \"TopicArn\":\"arn:aws:sns:us-east-2:123456789012:sns-lambda\",\n" + + " \"Subject\": \"TestInvoke\"\n" + " }\n" + " }\n" + " ]\n" + "}"; + + String sampleKinesisEvent = "{" + " \"Records\": [" + " {" + " \"kinesis\": {" + + " \"kinesisSchemaVersion\": \"1.0\"," + " \"partitionKey\": \"1\"," + + " \"sequenceNumber\": \"49590338271490256608559692538361571095921575989136588898\"," + + " \"data\": \"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==\"," + + " \"approximateArrivalTimestamp\": 1545084650.987" + " }," + + " \"eventSource\": \"aws:kinesis\"," + " \"eventVersion\": \"1.0\"," + + " \"eventID\": \"shardId-000000000006:49590338271490256608559692538361571095921575989136588898\"," + + " \"eventName\": \"aws:kinesis:record\"," + + " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\"," + + " \"awsRegion\": \"us-east-2\"," + + " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"" + + " }," + " {" + " \"kinesis\": {" + + " \"kinesisSchemaVersion\": \"1.0\"," + " \"partitionKey\": \"1\"," + + " \"sequenceNumber\": \"49590338271490256608559692540925702759324208523137515618\"," + + " \"data\": \"VGhpcyBpcyBvbmx5IGEgdGVzdC4=\"," + + " \"approximateArrivalTimestamp\": 1545084711.166" + " }," + + " \"eventSource\": \"aws:kinesis\"," + " \"eventVersion\": \"1.0\"," + + " \"eventID\": \"shardId-000000000006:49590338271490256608559692540925702759324208523137515618\"," + + " \"eventName\": \"aws:kinesis:record\"," + + " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\"," + + " \"awsRegion\": \"us-east-2\"," + + " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"" + + " }" + " ]" + "}"; + + // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html + String apiGatewayV2Event = "{\n" + " \"version\": \"2.0\",\n" + " \"routeKey\": \"$default\",\n" + + " \"rawPath\": \"/my/path\",\n" + + " \"rawQueryString\": \"parameter1=value1¶meter1=value2¶meter2=value\",\n" + " \"cookies\": [\n" + + " \"cookie1\",\n" + " \"cookie2\"\n" + " ],\n" + " \"headers\": {\n" + + " \"header1\": \"value1\",\n" + " \"header2\": \"value1,value2\"\n" + " },\n" + + " \"queryStringParameters\": {\n" + " \"parameter1\": \"value1,value2\",\n" + + " \"parameter2\": \"value\"\n" + " },\n" + " \"requestContext\": {\n" + + " \"accountId\": \"123456789012\",\n" + " \"apiId\": \"api-id\",\n" + " \"authentication\": {\n" + + " \"clientCert\": {\n" + " \"clientCertPem\": \"CERT_CONTENT\",\n" + + " \"subjectDN\": \"www.example.com\",\n" + " \"issuerDN\": \"Example issuer\",\n" + + " \"serialNumber\": \"a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1\",\n" + + " \"validity\": {\n" + " \"notBefore\": \"May 28 12:30:02 2019 GMT\",\n" + + " \"notAfter\": \"Aug 5 09:36:04 2021 GMT\"\n" + " }\n" + " }\n" + " },\n" + + " \"authorizer\": {\n" + " \"jwt\": {\n" + " \"claims\": {\n" + + " \"claim1\": \"value1\",\n" + " \"claim2\": \"value2\"\n" + " },\n" + + " \"scopes\": [\n" + " \"scope1\",\n" + " \"scope2\"\n" + " ]\n" + + " }\n" + " },\n" + " \"domainName\": \"id.execute-api.us-east-1.amazonaws.com\",\n" + + " \"domainPrefix\": \"id\",\n" + " \"http\": {\n" + " \"method\": \"POST\",\n" + + " \"path\": \"/my/path\",\n" + " \"protocol\": \"HTTP/1.1\",\n" + + " \"sourceIp\": \"IP\",\n" + " \"userAgent\": \"agent\"\n" + " },\n" + + " \"requestId\": \"id\",\n" + " \"routeKey\": \"$default\",\n" + " \"stage\": \"$default\",\n" + + " \"time\": \"12/Mar/2020:19:03:58 +0000\",\n" + " \"timeEpoch\": 1583348638390\n" + " },\n" + + " \"body\": \"Hello from Lambda\",\n" + " \"pathParameters\": {\n" + " \"parameter1\": \"value1\"\n" + + " },\n" + " \"isBase64Encoded\": false,\n" + " \"stageVariables\": {\n" + + " \"stageVariable1\": \"value1\",\n" + " \"stageVariable2\": \"value2\"\n" + " }\n" + "}"; + + String apiGatewayEvent = "{\n" + " \"resource\": \"/uppercase2\",\n" + " \"path\": \"/uppercase2\",\n" + + " \"httpMethod\": \"POST\",\n" + " \"headers\": {\n" + " \"accept\": \"*/*\",\n" + + " \"content-type\": \"application/json\",\n" + + " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"User-Agent\": \"curl/7.54.0\",\n" + + " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" + + " \"X-Forwarded-For\": \"90.37.8.133\",\n" + " \"X-Forwarded-Port\": \"443\",\n" + + " \"X-Forwarded-Proto\": \"https\"\n" + " },\n" + " \"multiValueHeaders\": {\n" + + " \"accept\": [\n" + " \"*/*\"\n" + " ],\n" + " \"content-type\": [\n" + + " \"application/json\"\n" + " ],\n" + " \"Host\": [\n" + + " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + " ],\n" + + " \"User-Agent\": [\n" + " \"curl/7.54.0\"\n" + " ],\n" + + " \"X-Amzn-Trace-Id\": [\n" + " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" + + " ],\n" + " \"X-Forwarded-For\": [\n" + " \"90.37.8.133\"\n" + " ],\n" + + " \"X-Forwarded-Port\": [\n" + " \"443\"\n" + " ],\n" + + " \"X-Forwarded-Proto\": [\n" + " \"https\"\n" + " ]\n" + " },\n" + + " \"queryStringParameters\": null,\n" + " \"multiValueQueryStringParameters\": null,\n" + + " \"pathParameters\": null,\n" + " \"stageVariables\": null,\n" + " \"requestContext\": {\n" + + " \"resourceId\": \"qf0io6\",\n" + " \"resourcePath\": \"/uppercase2\",\n" + + " \"httpMethod\": \"POST\",\n" + " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" + + " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + " \"path\": \"/test/uppercase2\",\n" + + " \"accountId\": \"123456789098\",\n" + " \"protocol\": \"HTTP/1.1\",\n" + + " \"stage\": \"test\",\n" + " \"domainPrefix\": \"fhul32ccy2\",\n" + + " \"requestTimeEpoch\": 1590571934872,\n" + + " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + " \"identity\": {\n" + + " \"cognitoIdentityPoolId\": null,\n" + " \"accountId\": null,\n" + + " \"cognitoIdentityId\": null,\n" + " \"caller\": null,\n" + + " \"sourceIp\": \"90.37.8.133\",\n" + " \"principalOrgId\": null,\n" + + " \"accessKey\": null,\n" + " \"cognitoAuthenticationType\": null,\n" + + " \"cognitoAuthenticationProvider\": null,\n" + " \"userArn\": null,\n" + + " \"userAgent\": \"curl/7.54.0\",\n" + " \"user\": null\n" + " },\n" + + " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"apiId\": \"fhul32ccy2\"\n" + " },\n" + " \"body\":\"hello\",\n" + + " \"isBase64Encoded\": false\n" + "}"; + + String s3Event = "{\n" + " \"Records\": [\n" + " {\n" + " \"eventVersion\": \"2.1\",\n" + + " \"eventSource\": \"aws:s3\",\n" + " \"awsRegion\": \"eu-central-1\",\n" + " \"eventTime\": \"2023-11-04T23:44:23.905Z\",\n" - + " \"eventName\": \"ObjectCreated:Put\",\n" - + " \"userIdentity\": {\n" - + " \"principalId\": \"AWS:xxxxxxxxxxxxxxxxxxx\"\n" - + " },\n" - + " \"requestParameters\": {\n" - + " \"sourceIPAddress\": \"x.x.x.x\"\n" - + " },\n" - + " \"responseElements\": {\n" + + " \"eventName\": \"ObjectCreated:Put\",\n" + " \"userIdentity\": {\n" + + " \"principalId\": \"AWS:xxxxxxxxxxxxxxxxxxx\"\n" + " },\n" + + " \"requestParameters\": {\n" + " \"sourceIPAddress\": \"x.x.x.x\"\n" + + " },\n" + " \"responseElements\": {\n" + " \"x-amz-request-id\": \"xxxxxxxxxxxxxxxx\",\n" - + " \"x-amz-id-2\": \"xxxxxxxxxxxxxxxxxxxx\"\n" - + " },\n" - + " \"s3\": {\n" - + " \"s3SchemaVersion\": \"1.0\",\n" - + " \"configurationId\": \"xxxxxxxxxxxxxxxxxxxxxxxx\",\n" - + " \"bucket\": {\n" - + " \"name\": \"xxxxxxxxxxxxxxx\",\n" - + " \"ownerIdentity\": {\n" - + " \"principalId\": \"xxxxxxxxxxxxxxxxxx\"\n" - + " },\n" - + " \"arn\": \"arn:aws:s3:::xxxxxxxxxxxxxxxxx\"\n" - + " },\n" - + " \"object\": {\n" - + " \"key\": \"xxxxxxxxxxxxxxxx\",\n" - + " \"size\": 6064,\n" - + " \"eTag\": \"xxxxxxxxxxxxx\",\n" - + " \"sequencer\": \"xxxxxxxxxxxxxx\"\n" - + " }\n" - + " }\n" - + " }\n" - + " ]\n" + + " \"x-amz-id-2\": \"xxxxxxxxxxxxxxxxxxxx\"\n" + " },\n" + + " \"s3\": {\n" + " \"s3SchemaVersion\": \"1.0\",\n" + + " \"configurationId\": \"xxxxxxxxxxxxxxxxxxxxxxxx\",\n" + " \"bucket\": {\n" + + " \"name\": \"xxxxxxxxxxxxxxx\",\n" + " \"ownerIdentity\": {\n" + + " \"principalId\": \"xxxxxxxxxxxxxxxxxx\"\n" + " },\n" + + " \"arn\": \"arn:aws:s3:::xxxxxxxxxxxxxxxxx\"\n" + " },\n" + + " \"object\": {\n" + " \"key\": \"xxxxxxxxxxxxxxxx\",\n" + + " \"size\": 6064,\n" + " \"eTag\": \"xxxxxxxxxxxxx\",\n" + + " \"sequencer\": \"xxxxxxxxxxxxxx\"\n" + " }\n" + " }\n" + + " }\n" + " ]\n" + "}"; + + String apiGatewayEventWithStructuredBody = "{\n" + " \"resource\": \"/uppercase2\",\n" + + " \"path\": \"/uppercase2\",\n" + " \"httpMethod\": \"POST\",\n" + " \"headers\": {\n" + + " \"accept\": \"*/*\",\n" + " \"content-type\": \"application/json\",\n" + + " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"User-Agent\": \"curl/7.54.0\",\n" + + " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" + + " \"X-Forwarded-For\": \"90.37.8.133\",\n" + " \"X-Forwarded-Port\": \"443\",\n" + + " \"X-Forwarded-Proto\": \"https\"\n" + " },\n" + " \"multiValueHeaders\": {\n" + + " \"accept\": [\n" + " \"*/*\"\n" + " ],\n" + " \"content-type\": [\n" + + " \"application/json\"\n" + " ],\n" + " \"Host\": [\n" + + " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + " ],\n" + + " \"User-Agent\": [\n" + " \"curl/7.54.0\"\n" + " ],\n" + + " \"X-Amzn-Trace-Id\": [\n" + " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" + + " ],\n" + " \"X-Forwarded-For\": [\n" + " \"90.37.8.133\"\n" + " ],\n" + + " \"X-Forwarded-Port\": [\n" + " \"443\"\n" + " ],\n" + + " \"X-Forwarded-Proto\": [\n" + " \"https\"\n" + " ]\n" + " },\n" + + " \"queryStringParameters\": null,\n" + " \"multiValueQueryStringParameters\": null,\n" + + " \"pathParameters\": null,\n" + " \"stageVariables\": null,\n" + " \"requestContext\": {\n" + + " \"resourceId\": \"qf0io6\",\n" + " \"resourcePath\": \"/uppercase2\",\n" + + " \"httpMethod\": \"POST\",\n" + " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" + + " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + " \"path\": \"/test/uppercase2\",\n" + + " \"accountId\": \"123456789098\",\n" + " \"protocol\": \"HTTP/1.1\",\n" + + " \"stage\": \"test\",\n" + " \"domainPrefix\": \"fhul32ccy2\",\n" + + " \"requestTimeEpoch\": 1590571934872,\n" + + " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + " \"identity\": {\n" + + " \"cognitoIdentityPoolId\": null,\n" + " \"accountId\": null,\n" + + " \"cognitoIdentityId\": null,\n" + " \"caller\": null,\n" + + " \"sourceIp\": \"90.37.8.133\",\n" + " \"principalOrgId\": null,\n" + + " \"accessKey\": null,\n" + " \"cognitoAuthenticationType\": null,\n" + + " \"cognitoAuthenticationProvider\": null,\n" + " \"userArn\": null,\n" + + " \"userAgent\": \"curl/7.54.0\",\n" + " \"user\": null\n" + " },\n" + + " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"apiId\": \"fhul32ccy2\"\n" + " },\n" + " \"body\":{\"name\":\"Jim Lahey\"},\n" + + " \"isBase64Encoded\": false\n" + "}"; + + String apiGatewayEventWithArray = "{\n" + " \"resource\": \"/uppercase2\",\n" + + " \"path\": \"/uppercase2\",\n" + " \"httpMethod\": \"POST\",\n" + " \"headers\": {\n" + + " \"accept\": \"*/*\",\n" + " \"content-type\": \"application/json\",\n" + + " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"User-Agent\": \"curl/7.54.0\",\n" + + " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" + + " \"X-Forwarded-For\": \"90.37.8.133\",\n" + " \"X-Forwarded-Port\": \"443\",\n" + + " \"X-Forwarded-Proto\": \"https\"\n" + " },\n" + " \"multiValueHeaders\": {\n" + + " \"accept\": [\n" + " \"*/*\"\n" + " ],\n" + " \"content-type\": [\n" + + " \"application/json\"\n" + " ],\n" + " \"Host\": [\n" + + " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + " ],\n" + + " \"User-Agent\": [\n" + " \"curl/7.54.0\"\n" + " ],\n" + + " \"X-Amzn-Trace-Id\": [\n" + " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" + + " ],\n" + " \"X-Forwarded-For\": [\n" + " \"90.37.8.133\"\n" + " ],\n" + + " \"X-Forwarded-Port\": [\n" + " \"443\"\n" + " ],\n" + + " \"X-Forwarded-Proto\": [\n" + " \"https\"\n" + " ]\n" + " },\n" + + " \"queryStringParameters\": null,\n" + " \"multiValueQueryStringParameters\": null,\n" + + " \"pathParameters\": null,\n" + " \"stageVariables\": null,\n" + " \"requestContext\": {\n" + + " \"resourceId\": \"qf0io6\",\n" + " \"resourcePath\": \"/uppercase2\",\n" + + " \"httpMethod\": \"POST\",\n" + " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" + + " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + " \"path\": \"/test/uppercase2\",\n" + + " \"accountId\": \"123456789098\",\n" + " \"protocol\": \"HTTP/1.1\",\n" + + " \"stage\": \"test\",\n" + " \"domainPrefix\": \"fhul32ccy2\",\n" + + " \"requestTimeEpoch\": 1590571934872,\n" + + " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + " \"identity\": {\n" + + " \"cognitoIdentityPoolId\": null,\n" + " \"accountId\": null,\n" + + " \"cognitoIdentityId\": null,\n" + " \"caller\": null,\n" + + " \"sourceIp\": \"90.37.8.133\",\n" + " \"principalOrgId\": null,\n" + + " \"accessKey\": null,\n" + " \"cognitoAuthenticationType\": null,\n" + + " \"cognitoAuthenticationProvider\": null,\n" + " \"userArn\": null,\n" + + " \"userAgent\": \"curl/7.54.0\",\n" + " \"user\": null\n" + " },\n" + + " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + + " \"apiId\": \"fhul32ccy2\"\n" + " },\n" + + " \"body\":[{\"name\":\"Jim Lahey\"},{\"name\":\"Ricky\"}],\n" + " \"isBase64Encoded\": false\n" + "}"; - String apiGatewayEventWithStructuredBody = "{\n" + - " \"resource\": \"/uppercase2\",\n" + - " \"path\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"headers\": {\n" + - " \"accept\": \"*/*\",\n" + - " \"content-type\": \"application/json\",\n" + - " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"User-Agent\": \"curl/7.54.0\",\n" + - " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" + - " \"X-Forwarded-For\": \"90.37.8.133\",\n" + - " \"X-Forwarded-Port\": \"443\",\n" + - " \"X-Forwarded-Proto\": \"https\"\n" + - " },\n" + - " \"multiValueHeaders\": {\n" + - " \"accept\": [\n" + - " \"*/*\"\n" + - " ],\n" + - " \"content-type\": [\n" + - " \"application/json\"\n" + - " ],\n" + - " \"Host\": [\n" + - " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + - " ],\n" + - " \"User-Agent\": [\n" + - " \"curl/7.54.0\"\n" + - " ],\n" + - " \"X-Amzn-Trace-Id\": [\n" + - " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" + - " ],\n" + - " \"X-Forwarded-For\": [\n" + - " \"90.37.8.133\"\n" + - " ],\n" + - " \"X-Forwarded-Port\": [\n" + - " \"443\"\n" + - " ],\n" + - " \"X-Forwarded-Proto\": [\n" + - " \"https\"\n" + - " ]\n" + - " },\n" + - " \"queryStringParameters\": null,\n" + - " \"multiValueQueryStringParameters\": null,\n" + - " \"pathParameters\": null,\n" + - " \"stageVariables\": null,\n" + - " \"requestContext\": {\n" + - " \"resourceId\": \"qf0io6\",\n" + - " \"resourcePath\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" + - " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + - " \"path\": \"/test/uppercase2\",\n" + - " \"accountId\": \"123456789098\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"stage\": \"test\",\n" + - " \"domainPrefix\": \"fhul32ccy2\",\n" + - " \"requestTimeEpoch\": 1590571934872,\n" + - " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + - " \"identity\": {\n" + - " \"cognitoIdentityPoolId\": null,\n" + - " \"accountId\": null,\n" + - " \"cognitoIdentityId\": null,\n" + - " \"caller\": null,\n" + - " \"sourceIp\": \"90.37.8.133\",\n" + - " \"principalOrgId\": null,\n" + - " \"accessKey\": null,\n" + - " \"cognitoAuthenticationType\": null,\n" + - " \"cognitoAuthenticationProvider\": null,\n" + - " \"userArn\": null,\n" + - " \"userAgent\": \"curl/7.54.0\",\n" + - " \"user\": null\n" + - " },\n" + - " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"apiId\": \"fhul32ccy2\"\n" + - " },\n" + - " \"body\":{\"name\":\"Jim Lahey\"},\n" + - " \"isBase64Encoded\": false\n" + - "}"; - - String apiGatewayEventWithArray = "{\n" + - " \"resource\": \"/uppercase2\",\n" + - " \"path\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"headers\": {\n" + - " \"accept\": \"*/*\",\n" + - " \"content-type\": \"application/json\",\n" + - " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"User-Agent\": \"curl/7.54.0\",\n" + - " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" + - " \"X-Forwarded-For\": \"90.37.8.133\",\n" + - " \"X-Forwarded-Port\": \"443\",\n" + - " \"X-Forwarded-Proto\": \"https\"\n" + - " },\n" + - " \"multiValueHeaders\": {\n" + - " \"accept\": [\n" + - " \"*/*\"\n" + - " ],\n" + - " \"content-type\": [\n" + - " \"application/json\"\n" + - " ],\n" + - " \"Host\": [\n" + - " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + - " ],\n" + - " \"User-Agent\": [\n" + - " \"curl/7.54.0\"\n" + - " ],\n" + - " \"X-Amzn-Trace-Id\": [\n" + - " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" + - " ],\n" + - " \"X-Forwarded-For\": [\n" + - " \"90.37.8.133\"\n" + - " ],\n" + - " \"X-Forwarded-Port\": [\n" + - " \"443\"\n" + - " ],\n" + - " \"X-Forwarded-Proto\": [\n" + - " \"https\"\n" + - " ]\n" + - " },\n" + - " \"queryStringParameters\": null,\n" + - " \"multiValueQueryStringParameters\": null,\n" + - " \"pathParameters\": null,\n" + - " \"stageVariables\": null,\n" + - " \"requestContext\": {\n" + - " \"resourceId\": \"qf0io6\",\n" + - " \"resourcePath\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" + - " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + - " \"path\": \"/test/uppercase2\",\n" + - " \"accountId\": \"123456789098\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"stage\": \"test\",\n" + - " \"domainPrefix\": \"fhul32ccy2\",\n" + - " \"requestTimeEpoch\": 1590571934872,\n" + - " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + - " \"identity\": {\n" + - " \"cognitoIdentityPoolId\": null,\n" + - " \"accountId\": null,\n" + - " \"cognitoIdentityId\": null,\n" + - " \"caller\": null,\n" + - " \"sourceIp\": \"90.37.8.133\",\n" + - " \"principalOrgId\": null,\n" + - " \"accessKey\": null,\n" + - " \"cognitoAuthenticationType\": null,\n" + - " \"cognitoAuthenticationProvider\": null,\n" + - " \"userArn\": null,\n" + - " \"userAgent\": \"curl/7.54.0\",\n" + - " \"user\": null\n" + - " },\n" + - " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"apiId\": \"fhul32ccy2\"\n" + - " },\n" + - " \"body\":[{\"name\":\"Jim Lahey\"},{\"name\":\"Ricky\"}],\n" + - " \"isBase64Encoded\": false\n" + - "}"; - - String gwAuthorizerEvent = "{\n" - + " \"type\":\"TOKEN\",\n" - + " \"authorizationToken\":\"allow\",\n" - + " \"methodArn\":\"arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/\"\n" - + "}"; + String gwAuthorizerEvent = "{\n" + " \"type\":\"TOKEN\",\n" + " \"authorizationToken\":\"allow\",\n" + + " \"methodArn\":\"arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/\"\n" + "}"; @BeforeEach public void before() throws Exception { System.clearProperty("MAIN_CLASS"); System.clearProperty("spring.cloud.function.routing-expression"); System.clearProperty("spring.cloud.function.definition"); - //this.getEnvironment().clear(); + // this.getEnvironment().clear(); } @Test @@ -1097,7 +713,6 @@ public void testS3EventAsMap() throws Exception { assertThat(result).contains("s3SchemaVersion"); } - @Test public void testLBEventStringInOut() throws Exception { System.setProperty("MAIN_CLASS", LBConfiguration.class.getName()); @@ -1174,7 +789,6 @@ public void handleRequest(InputStream input, OutputStream output, Context contex assertThat(result.get("body")).isEqualTo("Hello from ELB"); } - @SuppressWarnings("rawtypes") @Test public void testApiGatewayStringEventBody() throws Exception { @@ -1331,7 +945,8 @@ public void testResponseBase64Encoded() throws Exception { Map resultMap = mapper.fromJson(result, Map.class); assertThat((boolean) resultMap.get(AWSLambdaUtils.IS_BASE64_ENCODED)).isTrue(); assertThat((int) resultMap.get(AWSLambdaUtils.STATUS_CODE)).isEqualTo(201); - String body = new String(Base64.getDecoder().decode((String) resultMap.get(AWSLambdaUtils.BODY)), StandardCharsets.UTF_8); + String body = new String(Base64.getDecoder().decode((String) resultMap.get(AWSLambdaUtils.BODY)), + StandardCharsets.UTF_8); assertThat(body).isEqualTo("hello"); } @@ -1546,35 +1161,42 @@ public void testPrimitiveMessage() throws Exception { @EnableAutoConfiguration @Configuration public static class BasicConfiguration { + @Bean public Function, Message> uppercase() { return v -> { return MessageBuilder.withPayload(v.getPayload().toUpperCase(Locale.ROOT)).build(); }; } + } @EnableAutoConfiguration @Configuration public static class AuthorizerConfiguration { + @Bean public Function acceptAuthorizerEvent() { return v -> v.toString(); } + } @EnableAutoConfiguration @Configuration public static class DynamoDbConfiguration { + @Bean public Consumer consume() { return event -> event.getRecords().forEach(System.out::println); } + } @EnableAutoConfiguration @Configuration public static class SampleConfiguration { + @Bean public Function echoString() { return v -> v; @@ -1599,11 +1221,13 @@ public Function, Flux> echoStringReactive() { public Function, Flux> echoPojoReactive() { return v -> v; } + } @EnableAutoConfiguration @Configuration public static class KinesisConfiguration { + @Bean public Function echoString() { return v -> v; @@ -1632,11 +1256,13 @@ public Function, String> inputKinesisEventAsMap() { return v.toString(); }; } + } @EnableAutoConfiguration @Configuration public static class SQSConfiguration { + @Bean public Function echoString() { return v -> { @@ -1673,6 +1299,7 @@ public Function, String> inputSQSEventAsMap() { public MyCustomMessageConverter messageConverter() { return new MyCustomMessageConverter(); } + } public static class MyCustomMessageConverter extends AbstractMessageConverter { @@ -1694,11 +1321,13 @@ protected Object convertFromInternal(Message message, Class targetClass, O person.setName(v.substring(0, 10)); return person; } + } @EnableAutoConfiguration @Configuration public static class SNSConfiguration { + @Bean public Function echoString() { return v -> { @@ -1730,6 +1359,7 @@ public Function, String> inputSNSEventAsMap() { return v.toString(); }; } + } @EnableAutoConfiguration @@ -1742,6 +1372,7 @@ public Function outputS3Event() { return v; }; } + @Bean public Function echoString() { return v -> v; @@ -1775,11 +1406,13 @@ public Function, String> inputS3EventAsMap() { return v.toString(); }; } + } @EnableAutoConfiguration @Configuration public static class LBConfiguration { + @Bean public Function echoString() { return v -> v; @@ -1803,7 +1436,8 @@ public Function, String> inputLBEventAsMessage(JsonMapper jsonMapper) { + public Function, String> inputLBEventAsMessage( + JsonMapper jsonMapper) { return message -> { System.out.println("Received: " + message); assertThat(message.getHeaders().get(AWSLambdaUtils.AWS_CONTEXT)).isNotNull(); @@ -1818,6 +1452,7 @@ public Function, String> inputLBEventAsMap() { return v.toString(); }; } + } @EnableAutoConfiguration @@ -1832,15 +1467,15 @@ public Supplier supply() { @Bean public Function, Message> echoStringMessage() { return m -> { - String encodedPayload = Base64.getEncoder().encodeToString(m.getPayload().getBytes(StandardCharsets.UTF_8)); + String encodedPayload = Base64.getEncoder() + .encodeToString(m.getPayload().getBytes(StandardCharsets.UTF_8)); return MessageBuilder.withPayload(encodedPayload) - .setHeader("isBase64Encoded", true) - .setHeader("statusCode", 201) - .build(); + .setHeader("isBase64Encoded", true) + .setHeader("statusCode", 201) + .build(); }; } - @Bean public Consumer consume() { return v -> System.out.println(v); @@ -1969,35 +1604,36 @@ public Function, String> inputApiEventAsMap() { @Bean public Function, Mono> outputPolicyResponse() { - return input -> - input.map(v -> IamPolicyResponse.builder() - .withPrincipalId("principalId") - .withPolicyDocument(IamPolicyResponse.PolicyDocument.builder() - .withVersion("2012-10-17") - .withStatement( - List.of( - IamPolicyResponse.Statement.builder().withAction("execute-api:Invoke") - .withResource( - List.of(v)).withEffect("Allow").build() - ) - ).build() - ).build() - ); + return input -> input.map(v -> IamPolicyResponse.builder() + .withPrincipalId("principalId") + .withPolicyDocument(IamPolicyResponse.PolicyDocument.builder() + .withVersion("2012-10-17") + .withStatement(List.of(IamPolicyResponse.Statement.builder() + .withAction("execute-api:Invoke") + .withResource(List.of(v)) + .withEffect("Allow") + .build())) + .build()) + .build()); } + } @EnableAutoConfiguration @Configuration public static class PrimitiveConfiguration { + @Bean public Function, byte[]> returnByteArrayAsMessage() { return v -> { return v.getPayload(); }; } + } public static class Person { + private String name; public String getName() { @@ -2012,6 +1648,7 @@ public void setName(String name) { public String toString() { return this.name; } + } @EnableAutoConfiguration @@ -2025,5 +1662,7 @@ public Function event() { return event; }; } + } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/TestContext.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/TestContext.java index ba2d11f83..16141e507 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/TestContext.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/TestContext.java @@ -22,7 +22,6 @@ import com.amazonaws.services.lambda.runtime.LambdaLogger; /** - * * @author Oleg Zhurakousky * */ diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvoker.java index 4da763d0a..5997e1d54 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvoker.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvoker.java @@ -46,7 +46,6 @@ import org.springframework.util.StringUtils; /** - * * @author Christian Tzolov * @author Oleg Zhurakousky * @author Omer Celik @@ -57,6 +56,7 @@ public class AzureWebProxyInvoker implements FunctionInstanceInjector { private static Log logger = LogFactory.getLog(AzureWebProxyInvoker.class); private static final String AZURE_WEB_ADAPTER_NAME = "AzureWebAdapter"; + private static final String AZURE_WEB_ADAPTER_ROUTE = AZURE_WEB_ADAPTER_NAME + "/{e?}/{e2?}/{e3?}/{e4?}/{e5?}/{e6?}/{e7?}/{e8?}/{e9?}/{e10?}/{e11?}/{e12?}/{e13?}/{e14?}/{e15?}"; @@ -74,8 +74,8 @@ public T getInstance(Class functionClass) throws Exception { } /** - * Because the getInstance is called by Azure Java Function on every function request we need to cache the Spring - * context initialization on the first function call. + * Because the getInstance is called by Azure Java Function on every function request + * we need to cache the Spring context initialization on the first function call. * Double-Checked Locking Optimization was used to avoid unnecessary locking overhead. * @throws ServletException error. */ @@ -103,11 +103,10 @@ private HttpServletRequest prepareRequest(HttpRequestMessage> r ServerlessHttpServletRequest httpRequest = new ServerlessHttpServletRequest(servletContext, request.getHttpMethod().toString(), path); - request.getBody().ifPresent(body -> { - Charset charsetEncoding = request.getHeaders() != null && request.getHeaders().containsKey("content-encoding") - ? Charset.forName(request.getHeaders().get("content-encoding")) - : StandardCharsets.UTF_8; + Charset charsetEncoding = request.getHeaders() != null + && request.getHeaders().containsKey("content-encoding") + ? Charset.forName(request.getHeaders().get("content-encoding")) : StandardCharsets.UTF_8; httpRequest.setContent(body.getBytes(charsetEncoding)); }); @@ -126,13 +125,10 @@ private HttpServletRequest prepareRequest(HttpRequestMessage> r @FunctionName(AZURE_WEB_ADAPTER_NAME) public HttpResponseMessage execute( - @HttpTrigger(name = "req", methods = { - HttpMethod.GET, - HttpMethod.POST, - HttpMethod.PUT, - HttpMethod.DELETE, - HttpMethod.PATCH - }, authLevel = AuthorizationLevel.ANONYMOUS, route = AZURE_WEB_ADAPTER_ROUTE) HttpRequestMessage> request, + @HttpTrigger(name = "req", + methods = { HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.PATCH }, + authLevel = AuthorizationLevel.ANONYMOUS, + route = AZURE_WEB_ADAPTER_ROUTE) HttpRequestMessage> request, ExecutionContext context) { context.getLogger().info("Request body is: " + request.getBody().orElse("[empty]")); @@ -165,4 +161,5 @@ public HttpResponseMessage execute( } } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvokerTests.java index 42b79f600..0bfc8ba1a 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvokerTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvokerTests.java @@ -41,11 +41,10 @@ public void test() throws Exception { request.setHttpMethod(HttpMethod.GET); - request.setUri(new URI( - "http://localhost:7072/api/AzureWebAdapter/pets")); + request.setUri(new URI("http://localhost:7072/api/AzureWebAdapter/pets")); - request.setBody(Optional.of("{\"id\":\"535932f1-d18b-488a-ad8f-8d50b9678492\"" + - "\"breed\":\"Beagle\",\"name\":\"Murphy\",\"dateOfBirth\":1591682824313}")); + request.setBody(Optional.of("{\"id\":\"535932f1-d18b-488a-ad8f-8d50b9678492\"" + + "\"breed\":\"Beagle\",\"name\":\"Murphy\",\"dateOfBirth\":1591682824313}")); HttpResponseMessage response = instance.execute(request, new TestExecutionContext("execute")); @@ -56,9 +55,13 @@ public void test() throws Exception { public static class HttpRequestMessageStub implements HttpRequestMessage { private URI uri; + private HttpMethod httpMethod; + private Map headers; + private Map queryParameters; + private I body; public void setUri(URI uri) { @@ -121,7 +124,9 @@ public Builder createResponseBuilder(HttpStatus status) { public static class BuilderStub implements Builder { private HttpStatusType status; + private Map headers = new HashMap<>(); + private Object body; @Override @@ -152,11 +157,12 @@ public HttpResponseMessage build() { public static class HttpResponseMessageStub implements HttpResponseMessage { private HttpStatusType status; + private Map headers = new HashMap<>(); + private Object body; - HttpResponseMessageStub(HttpStatusType status, Map headers, - Object body) { + HttpResponseMessageStub(HttpStatusType status, Map headers, Object body) { this.status = status; this.headers = headers; this.body = body; @@ -178,4 +184,5 @@ public Object getBody() { } } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/Pet.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/Pet.java index abd92050d..5120fb7c3 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/Pet.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/Pet.java @@ -19,9 +19,13 @@ import java.util.Date; public class Pet { + private String id; + private String breed; + private String name; + private Date dateOfBirth; public String getId() { @@ -55,4 +59,5 @@ public Date getDateOfBirth() { public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } + } 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 dd221f394..c57a601da 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 @@ -16,7 +16,6 @@ package org.springframework.cloud.function.adapter.azure.web; - import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -25,9 +24,11 @@ import java.util.concurrent.ThreadLocalRandom; public final class PetData { + private PetData() { } + private static List breeds = new ArrayList<>(); static { breeds.add("Afghan Hound"); @@ -108,8 +109,8 @@ public static String getRandomName() { public static Date getRandomDoB() { GregorianCalendar gc = new GregorianCalendar(); - int year = ThreadLocalRandom.current().nextInt(Calendar.getInstance().get(Calendar.YEAR) - 15, - Calendar.getInstance().get(Calendar.YEAR)); + int year = ThreadLocalRandom.current() + .nextInt(Calendar.getInstance().get(Calendar.YEAR) - 15, Calendar.getInstance().get(Calendar.YEAR)); gc.set(Calendar.YEAR, year); @@ -118,4 +119,5 @@ public static Date getRandomDoB() { gc.set(Calendar.DAY_OF_YEAR, dayOfYear); return gc.getTime(); } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetStoreSpringAppConfig.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetStoreSpringAppConfig.java index 3ffaa3443..4e206bc45 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetStoreSpringAppConfig.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetStoreSpringAppConfig.java @@ -37,9 +37,10 @@ @Configuration @Import({ PetsController.class }) public class PetStoreSpringAppConfig { + /* - * Create required HandlerMapping, to avoid several default HandlerMapping - * instances being created + * Create required HandlerMapping, to avoid several default HandlerMapping instances + * being created */ @Bean public HandlerMapping handlerMapping() { @@ -47,8 +48,8 @@ public HandlerMapping handlerMapping() { } /* - * Create required HandlerAdapter, to avoid several default HandlerAdapter - * instances being created + * Create required HandlerAdapter, to avoid several default HandlerAdapter instances + * being created */ @Bean public HandlerAdapter handlerAdapter() { diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetsController.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetsController.java index 3748e1e12..0a1ccbbd7 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetsController.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetsController.java @@ -30,6 +30,7 @@ @RestController @EnableWebMvc public class PetsController { + @RequestMapping(path = "/pets", method = RequestMethod.POST) public Pet createPet(@RequestBody Pet newPet) { if (newPet.getName() == null || newPet.getBreed() == null) { @@ -73,4 +74,5 @@ public Pet listPets() { newPet.setName(PetData.getRandomName()); return newPet; } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionInstanceInjector.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionInstanceInjector.java index 209890652..26a6f4218 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionInstanceInjector.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionInstanceInjector.java @@ -32,10 +32,12 @@ import org.springframework.util.CollectionUtils; /** - * The instance factory used by the Spring framework to initialize Azure function instance. It is waived with the Azure - * Java Worker through the META-INFO/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector service - * hook. The Azure Java Worker delegates scans the classpath for service definition and delegates the function class - * creation to this instance factory. + * The instance factory used by the Spring framework to initialize Azure function + * instance. It is waived with the Azure Java Worker through the + * META-INFO/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector + * service hook. The Azure Java Worker delegates scans the classpath for service + * definition and delegates the function class creation to this instance factory. + * * @author Christian Tzolov * @author Omer Celik * @since 3.2.9 @@ -49,13 +51,16 @@ public class AzureFunctionInstanceInjector implements FunctionInstanceInjector { private static final ReentrantLock globalLock = new ReentrantLock(); /** - * This method is called by the Azure Java Worker on every function invocation. The Worker sends in the classes - * annotated with @FunctionName annotations and allows the Spring framework to initialize the function instance as a - * Spring Bean and return the instance. Then the Worker uses the created instance to invoke the function. - * @param functionClass the class that contains customer Azure functions (e.g. @FunctionName annotated ) + * This method is called by the Azure Java Worker on every function invocation. The + * Worker sends in the classes annotated with @FunctionName annotations and allows the + * Spring framework to initialize the function instance as a Spring Bean and return + * the instance. Then the Worker uses the created instance to invoke the function. + * @param functionClass the class that contains customer Azure functions + * (e.g. @FunctionName annotated ) * @param customer Azure functions class type * @return the instance that will be invoked on by azure functions java worker - * @throws Exception any exception that is thrown by the Spring framework during instance creation + * @throws Exception any exception that is thrown by the Spring framework during + * instance creation */ @Override public T getInstance(Class functionClass) throws Exception { @@ -65,9 +70,8 @@ public T getInstance(Class functionClass) throws Exception { Map azureFunctionBean = APPLICATION_CONTEXT.getBeansOfType(functionClass); if (CollectionUtils.isEmpty(azureFunctionBean)) { - throw new IllegalStateException( - "Failed to retrieve Bean instance for: " + functionClass - + ". The class should be annotated with @Component to let the Spring framework initialize it!"); + throw new IllegalStateException("Failed to retrieve Bean instance for: " + functionClass + + ". The class should be annotated with @Component to let the Spring framework initialize it!"); } return azureFunctionBean.entrySet().iterator().next().getValue(); } @@ -80,8 +84,9 @@ public T getInstance(Class functionClass) throws Exception { } /** - * Create a static Application Context instance shared between multiple function invocations. - * Double-Checked Locking Optimization was used to avoid unnecessary locking overhead. + * Create a static Application Context instance shared between multiple function + * invocations. Double-Checked Locking Optimization was used to avoid unnecessary + * locking overhead. */ private static void initialize() { if (APPLICATION_CONTEXT == null) { @@ -107,4 +112,5 @@ private static SpringApplication springApplication(Class configurationClass) new DefaultResourceLoader().getResource("classpath:/spring-azure-function-banner.txt"))); return application; } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionUtil.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionUtil.java index 041ccd5df..351822ca0 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionUtil.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionUtil.java @@ -55,7 +55,8 @@ public static Object enhanceInputIfNecessary(Object input, ExecutionContext return Flux.from((Publisher) input).map(item -> { if (item instanceof Message) { return MessageBuilder.fromMessage((Message) item) - .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build(); + .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext) + .build(); } else { return constructInputMessageFromItem(input, executionContext); @@ -64,7 +65,8 @@ public static Object enhanceInputIfNecessary(Object input, ExecutionContext } else if (input instanceof Message) { return MessageBuilder.fromMessage((Message) input) - .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build(); + .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext) + .build(); } else if (input instanceof Iterable) { return Flux.fromIterable((Iterable) input).map(item -> { @@ -79,8 +81,7 @@ private static Message constructInputMessageFromItem(Object input, Execut if (input instanceof HttpRequestMessage) { HttpRequestMessage requestMessage = (HttpRequestMessage) input; Object payload = requestMessage.getHttpMethod() != null - && requestMessage.getHttpMethod().equals(HttpMethod.GET) - ? requestMessage.getQueryParameters() + && requestMessage.getHttpMethod().equals(HttpMethod.GET) ? requestMessage.getQueryParameters() : requestMessage.getBody(); if (payload == null) { diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java index ee22219d5..b56403a1c 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java @@ -26,7 +26,9 @@ public class BuilderStub implements Builder { private HttpStatusType status; + private Map headers = new HashMap<>(); + private Object body; @Override diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java index 5a95d23c4..20365c6e8 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java @@ -29,9 +29,13 @@ public class HttpRequestMessageStub implements HttpRequestMessage { private URI uri; + private HttpMethod httpMethod; + private Map headers; + private Map queryParameters; + private I body; public void setUri(URI uri) { @@ -88,4 +92,5 @@ public HttpResponseMessage.Builder createResponseBuilder(HttpStatusType status) public Builder createResponseBuilder(HttpStatus status) { return new BuilderStub().status(status); } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java index 3948a5c4a..d369cb006 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java @@ -25,11 +25,12 @@ public class HttpResponseMessageStub implements HttpResponseMessage { private HttpStatusType status; + private Map headers = new HashMap<>(); + private Object body; - public HttpResponseMessageStub(HttpStatusType status, Map headers, - Object body) { + public HttpResponseMessageStub(HttpStatusType status, Map headers, Object body) { this.status = status; this.headers = headers; this.body = body; 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 97051ead9..521e16794 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 @@ -82,16 +82,16 @@ public static class MyAzureFunction { @FunctionName("hello") public String execute( - @HttpTrigger(name = "req", methods = { HttpMethod.GET, - HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, + @HttpTrigger(name = "req", methods = { HttpMethod.GET, HttpMethod.POST }, + authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, ExecutionContext context) { - Message enhancedRequest = (Message) AzureFunctionUtil.enhanceInputIfNecessary( - request.getBody().get(), - context); + Message enhancedRequest = (Message) AzureFunctionUtil + .enhanceInputIfNecessary(request.getBody().get(), context); return uppercase.apply(enhancedRequest); } + } @Configuration @@ -102,7 +102,7 @@ public static class MySpringConfig { public Function, String> uppercaseBean() { return message -> { ExecutionContext context = (ExecutionContext) message.getHeaders() - .get(AzureFunctionUtil.EXECUTION_CONTEXT); + .get(AzureFunctionUtil.EXECUTION_CONTEXT); Assertions.assertThat(context).isNotNull(); Assertions.assertThat(context.getFunctionName()).isEqualTo("hello"); @@ -111,5 +111,7 @@ public Function, String> uppercaseBean() { }; } + } + } 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 f9a6efaae..0c2665dfd 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 @@ -99,19 +99,21 @@ private static FunctionInstanceInjector initializeFunctionInstanceInjector() { } @Configuration - @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)}) + @ComponentScan( + excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public static class MyMainConfig { @Bean public Function, String> uppercase() { return message -> { ExecutionContext context = (ExecutionContext) message.getHeaders() - .get(AzureFunctionUtil.EXECUTION_CONTEXT); + .get(AzureFunctionUtil.EXECUTION_CONTEXT); Assertions.assertThat(context).isNotNull(); Assertions.assertThat(context.getFunctionName()).isEqualTo("hello"); return message.getPayload().toUpperCase(Locale.ROOT); }; } + } @Component @@ -122,14 +124,15 @@ public static class MyAzureTestFunction { @FunctionName("ditest") public String execute( - @HttpTrigger(name = "req", methods = { HttpMethod.GET, - HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, + @HttpTrigger(name = "req", methods = { HttpMethod.GET, HttpMethod.POST }, + authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, ExecutionContext context) { - Message enhancedRequest = (Message) AzureFunctionUtil.enhanceInputIfNecessary( - request.getBody().get(), - context); + Message enhancedRequest = (Message) AzureFunctionUtil + .enhanceInputIfNecessary(request.getBody().get(), context); return uppercase.apply(enhancedRequest); } + } + } 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 a84c15e33..6f1dc6cb6 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 @@ -70,7 +70,8 @@ public class FunctionInvoker implements HttpFunction, RawBackgroundFunction { private static final Log log = LogFactory.getLog(FunctionInvoker.class); /** - * Constant specifying Http Status Code. Accessible to users by calling 'FunctionInvoker.HTTP_STATUS_CODE' + * Constant specifying Http Status Code. Accessible to users by calling + * 'FunctionInvoker.HTTP_STATUS_CODE' */ public static final String HTTP_STATUS_CODE = "statusCode"; @@ -98,7 +99,7 @@ private void init(Class configurationClass) { System.setProperty(ContextFunctionCatalogAutoConfiguration.JSON_MAPPER_PROPERTY, "gson"); } Thread.currentThread() // TODO: remove after upgrading to 1.0.0-alpha-2-rc5 - .setContextClassLoader(FunctionInvoker.class.getClassLoader()); + .setContextClassLoader(FunctionInvoker.class.getClassLoader()); log.info("Initializing: " + configurationClass); SpringApplication springApplication = springApplication(configurationClass); @@ -124,14 +125,17 @@ public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws E Function, Message> function = lookupFunction(); Message message = this.functionWrapped.getInputType() == Void.class - || this.functionWrapped.getInputType() == null ? null - : MessageBuilder.withPayload(httpRequest.getReader()).copyHeaders(httpRequest.getHeaders()) - .build(); + || this.functionWrapped.getInputType() == null + ? null + : MessageBuilder.withPayload(httpRequest.getReader()) + .copyHeaders(httpRequest.getHeaders()) + .build(); Object resultObject = function.apply(message); if (resultObject != null) { - Message result = resultObject instanceof Publisher ? getResultFromPublisher(resultObject) : (Message) resultObject; + Message result = resultObject instanceof Publisher ? getResultFromPublisher(resultObject) + : (Message) resultObject; buildHttpResponse(httpRequest, httpResponse, result); } @@ -140,8 +144,7 @@ public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws E /** * The implementation of a GCF {@link RawBackgroundFunction} that will be used as the * entry point from GCF. - * - * @param json the payload. + * @param json the payload. * @param context event context. * @since 3.0.5 */ @@ -188,8 +191,9 @@ else if (result.getHeaders().containsKey("Content-Type")) { for (Entry header : headers.entrySet()) { Object values = header.getValue(); if (values instanceof Collection) { - String headerValue = ((Collection) values).stream().map(item -> item.toString()) - .collect(Collectors.joining(",")); + String headerValue = ((Collection) values).stream() + .map(item -> item.toString()) + .collect(Collectors.joining(",")); httpResponse.appendHeader(header.getKey(), headerValue); } else { @@ -210,7 +214,10 @@ else if (result.getHeaders().containsKey("Content-Type")) { /* * This methd get the result from reactor's publisher. * - * For reference: https://github.com/spring-cloud/spring-cloud-function/blob/main/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java + * For reference: + * https://github.com/spring-cloud/spring-cloud-function/blob/main/spring-cloud- + * function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/ + * springframework/cloud/function/adapter/aws/AWSLambdaUtils.java */ private Message getResultFromPublisher(Object resultObject) { List results = new ArrayList<>(); diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java index c759843d8..ca19722e7 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java @@ -26,8 +26,8 @@ import org.springframework.boot.loader.jar.JarFile; /** - * The launcher class written at the top-level of the output JAR to be deployed to - * Google Cloud Functions. This is the entry point to the function when run from JAR. + * The launcher class written at the top-level of the output JAR to be deployed to Google + * Cloud Functions. This is the entry point to the function when run from JAR. * * @author Ray Tsang * @author Daniel Zou @@ -43,10 +43,10 @@ public GcfJarLauncher() throws Exception { this.loader = createClassLoader(getClassPathArchivesIterator()); - Class clazz = this.loader - .loadClass("org.springframework.cloud.function.adapter.gcp.FunctionInvoker"); + Class clazz = this.loader.loadClass("org.springframework.cloud.function.adapter.gcp.FunctionInvoker"); this.delegate = clazz.getConstructor().newInstance(); } + @Override public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception { Thread.currentThread().setContextClassLoader(this.loader); @@ -58,5 +58,5 @@ public void accept(String json, Context context) throws Exception { Thread.currentThread().setContextClassLoader(this.loader); ((RawBackgroundFunction) delegate).accept(json, context); } -} +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java index 2aaf1b0ad..0f6cc30b1 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java @@ -50,7 +50,7 @@ public void writeLoadedClasses(LoaderClassesWriter writer) throws IOException { writer.writeLoaderClasses(LoaderImplementation.CLASSIC); String jarName = LAUNCHER_NAME.replaceAll("\\.", "/") + ".class"; - writer.writeEntry( - jarName, GcfJarLauncher.class.getResourceAsStream("/" + jarName)); + writer.writeEntry(jarName, GcfJarLauncher.class.getResourceAsStream("/" + jarName)); } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java index 0239c62e7..ee44dda60 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java @@ -33,4 +33,5 @@ public class GcfJarLayoutFactory implements LayoutFactory { public Layout getLayout(File source) { return new GcfJarLayout(); } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerBackgroundTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerBackgroundTests.java index 861c36d41..7257c926a 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerBackgroundTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerBackgroundTests.java @@ -54,6 +54,7 @@ public class FunctionInvokerBackgroundTests { public void testHelloWorldSupplier_Background(CaptureSystemOutput.OutputCapture outputCapture) { testBackgroundFunction(outputCapture, HelloWorldSupplier.class, null, "Hello World!", null, null); } + @Test public void testJsonInputFunction_Background(CaptureSystemOutput.OutputCapture outputCapture) { testBackgroundFunction(outputCapture, JsonInputFunction.class, new IncomingRequest("hello"), @@ -119,8 +120,8 @@ public void testPubSubBackgroundFunction_PubSubMessage(CaptureSystemOutput.Outpu "Message: Hello; Type: google.pubsub.topic.publish; Message ID: 1234", "google.pubsub.topic.publish"); } - private void testBackgroundFunction(CaptureSystemOutput.OutputCapture outputCapture, Class configurationClass, I input, O expectedResult, - String expectedSysOut, String eventType) { + private void testBackgroundFunction(CaptureSystemOutput.OutputCapture outputCapture, + Class configurationClass, I input, O expectedResult, String expectedSysOut, String eventType) { FunctionInvoker handler = new FunctionInvoker(configurationClass); @@ -192,8 +193,9 @@ protected static class JsonInputOutputFunction { @Bean public Function> function() { return (in) -> MessageBuilder - .withPayload(new OutgoingResponse("Thank you for sending the message: " + in.message)) - .setHeader("foo", "bar").build(); + .withPayload(new OutgoingResponse("Thank you for sending the message: " + in.message)) + .setHeader("foo", "bar") + .build(); } } @@ -263,6 +265,7 @@ public Consumer> consumeStringMessage(JsonMapper mapper) { System.out.println("Message: " + payload + "; Type: " + eventType + "; Message ID: " + messageId); }; } + } @Configuration @@ -299,6 +302,7 @@ public String getMessage() { public void setMessage(String message) { this.message = message; } + } public static class OutgoingResponse { 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 9b827a125..be002598d 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 @@ -63,9 +63,13 @@ public class FunctionInvokerHttpTests { private static final Gson gson = new Gson(); + private HttpRequest request; + private HttpResponse response; + private BufferedWriter bufferedWriter; + private StringWriter writer; @BeforeEach @@ -86,10 +90,8 @@ public void testHelloWorldSupplier() throws Exception { handler.service(request, response); bufferedWriter.close(); - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); - } @Test @@ -104,7 +106,6 @@ public void testJsonInputFunctionMono() throws Exception { handler.service(request, response); bufferedWriter.close(); - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); } @@ -119,7 +120,6 @@ public void testJsonInputFunctionFlux() throws Exception { handler.service(request, response); bufferedWriter.close(); - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); } @@ -135,7 +135,6 @@ public void testJsonInputFunction() throws Exception { handler.service(request, response); bufferedWriter.close(); - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); } @@ -151,7 +150,6 @@ public void testWithKanji() throws Exception { handler.service(request, response); bufferedWriter.close(); - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); } @@ -167,10 +165,8 @@ public void testJsonInputOutputFunction() throws Exception { handler.service(request, response); bufferedWriter.close(); - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); - } @Test @@ -233,7 +229,9 @@ public Function> function() { String payload = "hello"; - Message msg = MessageBuilder.withPayload(payload).setHeader("statusCode", 404).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain") + Message msg = MessageBuilder.withPayload(payload) + .setHeader("statusCode", 404) + .setHeader(MessageHeaders.CONTENT_TYPE, "text/plain") .build(); return x -> msg; @@ -251,8 +249,7 @@ public Function> function() { String payload = "hello"; List li = new ArrayList(asList(123, "headerThing")); - Message msg = MessageBuilder.withPayload(payload).setHeader("multiValueHeader", li) - .build(); + Message msg = MessageBuilder.withPayload(payload).setHeader("multiValueHeader", li).build(); return x -> msg; }; @@ -300,10 +297,12 @@ protected static class JsonInputOutputFunction { public Function> function() { return (in) -> { return MessageBuilder - .withPayload(new OutgoingResponse("Thank you for sending the message: " + in.message)) - .setHeader("foo", "bar").build(); + .withPayload(new OutgoingResponse("Thank you for sending the message: " + in.message)) + .setHeader("foo", "bar") + .build(); }; } + } @Configuration @@ -335,6 +334,7 @@ public String getMessage() { public void setMessage(String message) { this.message = message; } + } public static class OutgoingResponse { @@ -358,5 +358,4 @@ public OutgoingResponse() { } - } 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 a5f61c612..f875026ef 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 @@ -63,15 +63,14 @@ public void testFooBar() { @Test public void testErrorResponse() { - try (LocalServerTestSupport.ServerProcess serverProcess = - LocalServerTestSupport.startServer(ErrorFunction.class, "errorFunction")) { + try (LocalServerTestSupport.ServerProcess serverProcess = LocalServerTestSupport + .startServer(ErrorFunction.class, "errorFunction")) { TestRestTemplate testRestTemplate = new TestRestTemplate(); HttpHeaders headers = new HttpHeaders(); ResponseEntity response = testRestTemplate.postForEntity( - "http://localhost:" + serverProcess.getPort(), new HttpEntity<>("test", headers), - String.class); + "http://localhost:" + serverProcess.getPort(), new HttpEntity<>("test", headers), String.class); assertThat(response.getStatusCode().is5xxServerError()).isTrue(); } @@ -93,6 +92,7 @@ Supplier errorFunction() { throw new RuntimeException(); }; } + } @Configuration @@ -161,6 +161,7 @@ public String getValue() { public void setValue(String value) { this.value = value; } + } } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/LocalServerTestSupport.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/LocalServerTestSupport.java index 4ab2bcefa..1866720b2 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/LocalServerTestSupport.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/LocalServerTestSupport.java @@ -35,7 +35,6 @@ import com.google.cloud.functions.invoker.runner.Invoker; import com.google.gson.Gson; - import org.springframework.boot.resttestclient.TestRestTemplate; import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.cloud.function.adapter.gcp.FunctionInvoker; @@ -133,7 +132,8 @@ private static Integer monitorOutput(InputStream processOutput) { while ((line = reader.readLine()) != null) { System.out.println(line); if (line.contains(SERVER_READY_STRING)) { - // Started ServerConnector@192b07fd{HTTP/1.1,[http/1.1]}{0.0.0.0:59259} + // Started + // ServerConnector@192b07fd{HTTP/1.1,[http/1.1]}{0.0.0.0:59259} String portStr = line.substring(line.lastIndexOf(':') + 1, line.lastIndexOf('}')); return Integer.parseInt(portStr); } diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventGrpcAutoConfiguration.java b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventGrpcAutoConfiguration.java index 9403c03e7..b26fd0e82 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventGrpcAutoConfiguration.java +++ b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventGrpcAutoConfiguration.java @@ -25,7 +25,6 @@ import org.springframework.context.annotation.Configuration; /** - * * @author Oleg Zhurakousky * */ @@ -44,4 +43,5 @@ public BindableService cloudEventMessageHandler(MessageHandlingHelper helper) { public CloudEventMessageConverter cloudEventMessageConverter() { return new CloudEventMessageConverter(); } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventHandler.java b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventHandler.java index f737a03b4..2f68b9319 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventHandler.java +++ b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventHandler.java @@ -25,30 +25,25 @@ import org.springframework.cloud.function.grpc.MessageHandlingHelper; /** - * * @author Oleg Zhurakousky * @since 3.2 * */ @SuppressWarnings("rawtypes") -class CloudEventHandler extends CloudEventServiceImplBase { +class CloudEventHandler extends CloudEventServiceImplBase { private Log logger = LogFactory.getLog(CloudEventHandler.class); private final MessageHandlingHelper helper; - - CloudEventHandler(MessageHandlingHelper helper) { this.helper = helper; } - @SuppressWarnings("unchecked") @Override public void requestReply(CloudEvent request, StreamObserver responseObserver) { this.helper.requestReply(request, responseObserver); } -} - +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventMessageConverter.java b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventMessageConverter.java index 5284fb45d..09d914ec2 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventMessageConverter.java +++ b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventMessageConverter.java @@ -31,7 +31,6 @@ import org.springframework.messaging.support.MessageBuilder; /** - * * @author Oleg Zhurakousky * */ @@ -80,15 +79,15 @@ else if (attrCase.equals(AttrCase.CE_URI_REF)) { @Override protected CloudEvent doFromSpringMessage(Message springMessage) { Builder builder = CloudEvent.newBuilder() - .setTextDataBytes(ByteString.copyFrom(springMessage.getPayload())) - .setType(CloudEventMessageUtils.getType(springMessage)) - .setSource(CloudEventMessageUtils.getSource(springMessage).toString()) - .setId(CloudEventMessageUtils.getId(springMessage)) - .setSpecVersion(CloudEventMessageUtils.getSpecVersion(springMessage)); - + .setTextDataBytes(ByteString.copyFrom(springMessage.getPayload())) + .setType(CloudEventMessageUtils.getType(springMessage)) + .setSource(CloudEventMessageUtils.getSource(springMessage).toString()) + .setId(CloudEventMessageUtils.getId(springMessage)) + .setSpecVersion(CloudEventMessageUtils.getSpecVersion(springMessage)); for (Entry entry : springMessage.getHeaders().entrySet()) { - builder.putAttributes(entry.getKey(), CloudEventAttributeValue.newBuilder().setCeString(entry.getValue().toString()).build()); + builder.putAttributes(entry.getKey(), + CloudEventAttributeValue.newBuilder().setCeString(entry.getValue().toString()).build()); } return builder.build(); @@ -98,4 +97,5 @@ protected CloudEvent doFromSpringMessage(Message springMessage) { protected boolean supports(Class grpcClass) { return grpcClass.isAssignableFrom(CloudEvent.class); } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/test/java/org/springframework/cloud/grpc/ce/SpringCloudFunctionGrpcCloudeventApplicationTests.java b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/test/java/org/springframework/cloud/grpc/ce/SpringCloudFunctionGrpcCloudeventApplicationTests.java index 9593f6799..327faeab1 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/test/java/org/springframework/cloud/grpc/ce/SpringCloudFunctionGrpcCloudeventApplicationTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/test/java/org/springframework/cloud/grpc/ce/SpringCloudFunctionGrpcCloudeventApplicationTests.java @@ -21,8 +21,8 @@ @SpringBootTest class SpringCloudFunctionGrpcCloudeventApplicationTests { -// @Test -// void contextLoads() { -// } + // @Test + // void contextLoads() { + // } } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/AWSTypesProcessor.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/AWSTypesProcessor.java index 7e078c344..9f0402a8f 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/AWSTypesProcessor.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/AWSTypesProcessor.java @@ -38,16 +38,22 @@ public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableL return new ReflectiveProcessorBeanFactoryInitializationAotContribution(); } - private static final class ReflectiveProcessorBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution { + private static final class ReflectiveProcessorBeanFactoryInitializationAotContribution + implements BeanFactoryInitializationAotContribution { + @Override - public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) { + public void applyTo(GenerationContext generationContext, + BeanFactoryInitializationCode beanFactoryInitializationCode) { RuntimeHints runtimeHints = generationContext.getRuntimeHints(); // known static types - runtimeHints.reflection().registerType(HttpEntity.class, - MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); - runtimeHints.reflection().registerType(ResponseEntity.class, - MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + runtimeHints.reflection() + .registerType(HttpEntity.class, MemberCategory.INVOKE_PUBLIC_METHODS, + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + runtimeHints.reflection() + .registerType(ResponseEntity.class, MemberCategory.INVOKE_PUBLIC_METHODS, + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); } } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/FunctionClassUtils.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/FunctionClassUtils.java index c97d23cbf..13e4578b7 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/FunctionClassUtils.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/FunctionClassUtils.java @@ -34,8 +34,8 @@ import org.springframework.util.StringUtils; /** - * General utility class which aggregates various class-level utility functions - * used by the framework. + * General utility class which aggregates various class-level utility functions used by + * the framework. * * @author Oleg Zhurakousky * @since 3.0.1 @@ -51,11 +51,10 @@ private FunctionClassUtils() { } /** - * Discovers the start class in the currently running application. - * The discover search order is 'MAIN_CLASS' environment property, - * 'MAIN_CLASS' system property, META-INF/MANIFEST.MF:'Start-Class' attribute, - * meta-inf/manifest.mf:'Start-Class' attribute. - * + * Discovers the start class in the currently running application. The discover search + * order is 'MAIN_CLASS' environment property, 'MAIN_CLASS' system property, + * META-INF/MANIFEST.MF:'Start-Class' attribute, meta-inf/manifest.mf:'Start-Class' + * attribute. * @return instance of Class which represent the start class of the application. */ public static Class getStartClass() { @@ -76,11 +75,11 @@ else if (System.getProperty("MAIN_CLASS") != null) { } else { try { - Class result = getStartClass( - Collections.list(classLoader.getResources(JarFile.MANIFEST_NAME)), classLoader); + Class result = getStartClass(Collections.list(classLoader.getResources(JarFile.MANIFEST_NAME)), + classLoader); if (result == null) { - result = getStartClass(Collections - .list(classLoader.getResources("meta-inf/manifest.mf")), classLoader); + result = getStartClass(Collections.list(classLoader.getResources("meta-inf/manifest.mf")), + classLoader); } Assert.notNull(result, "Failed to locate main class"); mainClass = result; @@ -117,12 +116,15 @@ private static Class getStartClass(List list, ClassLoader classLoader) { Class startClass = ClassUtils.forName(startClassName, classLoader); if (KotlinDetector.isKotlinType(startClass)) { - PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver(classLoader); + PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver( + classLoader); String packageName = startClass.getPackage().getName(); - Resource[] resources = r.getResources("classpath:" + packageName.replace(".", "/") + "/*.class"); + Resource[] resources = r + .getResources("classpath:" + packageName.replace(".", "/") + "/*.class"); for (int i = 0; i < resources.length; i++) { Resource resource = resources[i]; - String className = packageName + "." + (resource.getFilename().replace("/", ".")).replace(".class", ""); + String className = packageName + "." + + (resource.getFilename().replace("/", ".")).replace(".class", ""); startClass = ClassUtils.forName(className, classLoader); } } @@ -140,4 +142,5 @@ private static Class getStartClass(List list, ClassLoader classLoader) { } return null; } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAsyncContext.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAsyncContext.java index f50c4826b..e04d985d8 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAsyncContext.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAsyncContext.java @@ -43,6 +43,7 @@ * @author Omer Celik */ public class ServerlessAsyncContext implements AsyncContext { + private final HttpServletRequest request; @Nullable @@ -59,13 +60,11 @@ public class ServerlessAsyncContext implements AsyncContext { private final ReentrantLock globalLock = new ReentrantLock(); - public ServerlessAsyncContext(ServletRequest request, @Nullable ServletResponse response) { this.request = (HttpServletRequest) request; this.response = (HttpServletResponse) response; } - public void addDispatchHandler(Runnable handler) { Assert.notNull(handler, "Dispatch handler must not be null"); try { @@ -95,7 +94,8 @@ public ServletResponse getResponse() { @Override public boolean hasOriginalRequestAndResponse() { - return (this.request instanceof ServerlessHttpServletRequest && this.response instanceof ServerlessHttpServletResponse); + return (this.request instanceof ServerlessHttpServletRequest + && this.response instanceof ServerlessHttpServletResponse); } @Override @@ -127,7 +127,8 @@ public String getDispatchedPath() { @Override public void complete() { - ServerlessHttpServletRequest mockRequest = WebUtils.getNativeRequest(this.request, ServerlessHttpServletRequest.class); + ServerlessHttpServletRequest mockRequest = WebUtils.getNativeRequest(this.request, + ServerlessHttpServletRequest.class); if (mockRequest != null) { mockRequest.setAsyncStarted(false); } @@ -166,13 +167,13 @@ public T createListener(Class clazz) throws Servlet } /** - * By default this is set to 10000 (10 seconds) even though the Servlet API - * specifies a default async request timeout of 30 seconds. Keep in mind the - * timeout could further be impacted by global configuration through the MVC - * Java config or the XML namespace, as well as be overridden per request on + * By default this is set to 10000 (10 seconds) even though the Servlet API specifies + * a default async request timeout of 30 seconds. Keep in mind the timeout could + * further be impacted by global configuration through the MVC Java config or the XML + * namespace, as well as be overridden per request on * {@link org.springframework.web.context.request.async.DeferredResult DeferredResult} - * or on - * {@link org.springframework.web.servlet.mvc.method.annotation.SseEmitter SseEmitter}. + * or on {@link org.springframework.web.servlet.mvc.method.annotation.SseEmitter + * SseEmitter}. * @param timeout the timeout value to use. * @see AsyncContext#setTimeout(long) */ @@ -185,4 +186,5 @@ public void setTimeout(long timeout) { public long getTimeout() { return this.timeout; } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAutoConfiguration.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAutoConfiguration.java index 2183b28c2..6c94f2c85 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAutoConfiguration.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAutoConfiguration.java @@ -41,6 +41,7 @@ */ @Configuration(proxyBeanMethods = false) public class ServerlessAutoConfiguration { + private static Log logger = LogFactory.getLog(ServerlessAutoConfiguration.class); @Bean @@ -94,7 +95,8 @@ public void afterPropertiesSet() throws Exception { catch (Exception e) { throw new IllegalStateException("Failed to create Spring MVC DispatcherServlet proxy", e); } - for (ServletContextInitializer initializer : new ServletContextInitializerBeans(this.applicationContext)) { + for (ServletContextInitializer initializer : new ServletContextInitializerBeans( + this.applicationContext)) { initializer.onStartup(servletContext); } } @@ -102,5 +104,7 @@ public void afterPropertiesSet() throws Exception { logger.debug("Skipping Serverless configuration for " + this.applicationContext); } } + } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessFilterRegistration.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessFilterRegistration.java index cca169618..e973d7786 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessFilterRegistration.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessFilterRegistration.java @@ -26,7 +26,6 @@ import jakarta.servlet.FilterRegistration; /** - * * @author Oleg Zhurakousky * @since 4.x * 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 a2c5e56f2..ef3c83c29 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 @@ -67,7 +67,6 @@ import org.springframework.util.MultiValueMap; /** - * * @author Oleg Zhurakousky * */ @@ -78,11 +77,12 @@ 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. * - * @see Section - * 7.1.1.1 of RFC 7231 + * @see Section 7.1.1.1 + * of RFC 7231 */ private static final String[] DATE_FORMATS = new String[] { "EEE, dd MMM yyyy HH:mm:ss zzz", "EEE, dd-MMM-yy HH:mm:ss zzz", "EEE MMM dd HH:mm:ss yyyy" }; @@ -180,8 +180,8 @@ public String toString() { } /** - * Return the ServletContext that this request is associated with. (Not - * available in the standard HttpServletRequest interface for some reason.) + * Return the ServletContext that this request is associated with. (Not available in + * the standard HttpServletRequest interface for some reason.) */ @Override public ServletContext getServletContext() { @@ -213,8 +213,7 @@ public void setCharacterEncoding(@Nullable String characterEncoding) { * Set the content of the request body as a byte array. *

* If the supplied byte array represents text such as XML or JSON, the - * {@link #setCharacterEncoding character encoding} should typically be set as - * well. + * {@link #setCharacterEncoding character encoding} should typically be set as well. * * @see #setCharacterEncoding(String) * @see #getContentAsByteArray() @@ -228,7 +227,6 @@ public void setContent(@Nullable byte[] content) { /** * Get the content of the request body as a byte array. - * * @return the content as a byte array (potentially {@code null}) * @since 5.0 * @see #setContent(byte[]) @@ -242,12 +240,9 @@ public byte[] getContentAsByteArray() { /** * Get the content of the request body as a {@code String}, using the configured * {@linkplain #getCharacterEncoding character encoding}. - * * @return the content as a {@code String}, potentially {@code null} - * @throws IllegalStateException if the character encoding has not been - * set - * @throws UnsupportedEncodingException if the character encoding is not - * supported + * @throws IllegalStateException if the character encoding has not been set + * @throws UnsupportedEncodingException if the character encoding is not supported * @since 5.0 * @see #setContent(byte[]) * @see #setCharacterEncoding(String) @@ -279,7 +274,8 @@ public void setContentType(@Nullable String contentType) { @Override @Nullable public String getContentType() { - return this.headers.containsHeader(HttpHeaders.CONTENT_TYPE) ? this.headers.get(HttpHeaders.CONTENT_TYPE).get(0) : null; + return this.headers.containsHeader(HttpHeaders.CONTENT_TYPE) ? this.headers.get(HttpHeaders.CONTENT_TYPE).get(0) + : null; } @Override @@ -325,8 +321,8 @@ public boolean isFinished() { /** * Set a single value for the specified HTTP parameter. *

- * If there are already one or more values registered for the given parameter - * name, they will be replaced. + * If there are already one or more values registered for the given parameter name, + * they will be replaced. */ public void setParameter(String name, String value) { setParameter(name, new String[] { value }); @@ -335,8 +331,8 @@ public void setParameter(String name, String value) { /** * Set an array of values for the specified HTTP parameter. *

- * If there are already one or more values registered for the given parameter - * name, they will be replaced. + * If there are already one or more values registered for the given parameter name, + * they will be replaced. */ public void setParameter(String name, String... values) { Assert.notNull(name, "Parameter name must not be null"); @@ -344,9 +340,9 @@ public void setParameter(String name, String... values) { } /** - * Set all provided parameters replacing any existing values - * for the provided parameter names. To add without replacing existing values, - * use {@link #addParameters(java.util.Map)}. + * Set all provided parameters replacing any existing values for the + * provided parameter names. To add without replacing existing values, use + * {@link #addParameters(java.util.Map)}. */ public void setParameters(Map params) { Assert.notNull(params, "Parameter map must not be null"); @@ -367,8 +363,8 @@ else if (value instanceof String[]) { /** * Add a single value for the specified HTTP parameter. *

- * If there are already one or more values registered for the given parameter - * name, the given value will be added to the end of the list. + * If there are already one or more values registered for the given parameter name, + * the given value will be added to the end of the list. */ public void addParameter(String name, @Nullable String value) { addParameter(name, new String[] { value }); @@ -377,8 +373,8 @@ public void addParameter(String name, @Nullable String value) { /** * Add an array of values for the specified HTTP parameter. *

- * If there are already one or more values registered for the given parameter - * name, the given values will be added to the end of the list. + * If there are already one or more values registered for the given parameter name, + * the given values will be added to the end of the list. */ public void addParameter(String name, String... values) { Assert.notNull(name, "Parameter name must not be null"); @@ -395,9 +391,8 @@ public void addParameter(String name, String... values) { } /** - * Add all provided parameters without replacing any existing - * values. To replace existing values, use - * {@link #setParameters(java.util.Map)}. + * Add all provided parameters without replacing any existing values. + * To replace existing values, use {@link #setParameters(java.util.Map)}. */ public void addParameters(Map params) { Assert.notNull(params, "Parameter map must not be null"); @@ -495,8 +490,7 @@ else if (this.inputStream != null) { if (this.content != null) { InputStream sourceStream = new ByteArrayInputStream(this.content); Reader sourceReader = (this.characterEncoding != null) - ? new InputStreamReader(sourceStream, this.characterEncoding) - : new InputStreamReader(sourceStream); + ? new InputStreamReader(sourceStream, this.characterEncoding) : new InputStreamReader(sourceStream); this.reader = new BufferedReader(sourceReader); } else { @@ -569,8 +563,8 @@ public Locale getLocale() { } /** - * Return an {@linkplain Enumeration enumeration} of the preferred - * {@linkplain Locale locales} configured in this mock request. + * Return an {@linkplain Enumeration enumeration} of the preferred {@linkplain Locale + * locales} configured in this mock request. *

* If no locales have been explicitly configured, the default, preferred * {@link Locale} for the server mocked by this request is @@ -651,7 +645,8 @@ public AsyncContext startAsync(ServletRequest request, @Nullable ServletResponse Assert.state(this.asyncSupported, "Async not supported"); this.dispatcherType = DispatcherType.ASYNC; this.asyncStarted = true; - this.asyncContext = this.asyncContext == null ? new ServerlessAsyncContext(request, response) : this.asyncContext; + this.asyncContext = this.asyncContext == null ? new ServerlessAsyncContext(request, response) + : this.asyncContext; return this.asyncContext; } @@ -684,7 +679,6 @@ public AsyncContext getAsyncContext() { return this.asyncContext; } - public void setDispatcherType(DispatcherType dispatcherType) { this.dispatcherType = dispatcherType; } @@ -830,7 +824,7 @@ public String getPathInfo() { @Override @Nullable public String getPathTranslated() { - //return (this.pathInfo != null ? getRealPath(this.pathInfo) : null); + // return (this.pathInfo != null ? getRealPath(this.pathInfo) : null); return this.pathInfo; } @@ -1025,4 +1019,5 @@ public ServletConnection getServletConnection() { // TODO Auto-generated method stub return null; } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpServletResponse.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpServletResponse.java index 58e433267..ee92fffdf 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpServletResponse.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpServletResponse.java @@ -44,9 +44,7 @@ import org.springframework.util.Assert; import org.springframework.web.util.WebUtils; - /** - * * @author Oleg Zhurakousky * @since 4.x * @@ -118,12 +116,10 @@ public byte[] getContentAsByteArray() { * specified for the response by the application, either through * {@link HttpServletResponse} methods or through a charset parameter on the * {@code Content-Type}. If no charset has been explicitly defined, the - * {@linkplain #setDefaultCharacterEncoding(String) default character encoding} - * will be used. - * + * {@linkplain #setDefaultCharacterEncoding(String) default character encoding} will + * be used. * @return the content as a {@code String} - * @throws UnsupportedEncodingException if the character encoding is not - * supported + * @throws UnsupportedEncodingException if the character encoding is not supported * @see #getContentAsString(Charset) * @see #setCharacterEncoding(String) * @see #setContentType(String) @@ -230,11 +226,9 @@ public boolean containsHeader(String name) { /** * Return the names of all specified headers as a Set of Strings. *

- * As of Servlet 3.0, this method is also defined in - * {@link HttpServletResponse}. - * - * @return the {@code Set} of header name {@code Strings}, or an empty - * {@code Set} if none + * As of Servlet 3.0, this method is also defined in {@link HttpServletResponse}. + * @return the {@code Set} of header name {@code Strings}, or an empty {@code Set} if + * none */ @Override public Collection getHeaderNames() { @@ -252,14 +246,12 @@ public Supplier> getTrailerFields() { } /** - * Return the primary value for the given header as a String, if any. Will - * return the first value in case of multiple values. + * Return the primary value for the given header as a String, if any. Will return the + * first value in case of multiple values. *

- * As of Servlet 3.0, this method is also defined in - * {@link HttpServletResponse}. As of Spring 3.1, it returns a stringified value - * for Servlet 3.0 compatibility. Consider using {@link #getHeaderValue(String)} - * for raw Object access. - * + * As of Servlet 3.0, this method is also defined in {@link HttpServletResponse}. As + * of Spring 3.1, it returns a stringified value for Servlet 3.0 compatibility. + * Consider using {@link #getHeaderValue(String)} for raw Object access. * @param name the name of the header * @return the associated header value, or {@code null} if none */ @@ -272,11 +264,9 @@ public String getHeader(String name) { /** * Return all values for the given header as a List of Strings. *

- * As of Servlet 3.0, this method is also defined in - * {@link HttpServletResponse}. As of Spring 3.1, it returns a List of - * stringified values for Servlet 3.0 compatibility. Consider using - * {@link #getHeaders(String)} for raw Object access. - * + * As of Servlet 3.0, this method is also defined in {@link HttpServletResponse}. As + * of Spring 3.1, it returns a List of stringified values for Servlet 3.0 + * compatibility. Consider using {@link #getHeaders(String)} for raw Object access. * @param name the name of the header * @return the associated header values, or an empty List if none */ @@ -292,7 +282,6 @@ public List getHeaders(String name) { * Return the primary value for the given header, if any. *

* Will return the first value in case of multiple values. - * * @param name the name of the header * @return the associated header value, or {@code null} if none */ @@ -312,13 +301,13 @@ public String encodeURL(String url) { } /** - * The default implementation delegates to {@link #encodeURL}, returning the - * given URL String as-is. + * The default implementation delegates to {@link #encodeURL}, returning the given URL + * String as-is. *

* Can be overridden in subclasses, appending a session id or the like in a - * redirect-specific fashion. For general URL encoding rules, override the - * common {@link #encodeURL} method instead, applying to redirect URLs as well - * as to general URLs. + * redirect-specific fashion. For general URL encoding rules, override the common + * {@link #encodeURL} method instead, applying to redirect URLs as well as to general + * URLs. */ @Override public String encodeRedirectURL(String url) { @@ -346,7 +335,6 @@ public void sendRedirect(String url) throws IOException { setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); } - @Override public void sendRedirect(String location, int sc, boolean clearBuffer) throws IOException { Assert.state(!isCommitted(), "Cannot send redirect - response is already committed"); @@ -406,7 +394,6 @@ public void setStatus(int status) { } } - @Override public int getStatus() { return this.status; @@ -418,8 +405,8 @@ public String getErrorMessage() { } /** - * Inner class that adapts the ServletOutputStream to mark the response as - * committed once the buffer size is exceeded. + * Inner class that adapts the ServletOutputStream to mark the response as committed + * once the buffer size is exceeded. */ private class ResponseServletOutputStream extends ServletOutputStream { @@ -461,6 +448,7 @@ public void close() throws IOException { super.close(); flushBuffer(); } + } private class ResponsePrintWriter extends PrintWriter { @@ -504,6 +492,7 @@ public void close() { super.close(); this.commited = true; } + } } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpSession.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpSession.java index 913d01d04..32fa2f572 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpSession.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpSession.java @@ -27,7 +27,6 @@ import jakarta.servlet.http.HttpSession; /** - * * @author Oleg Zhurakousky * @since 4.x * 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 0c4e57021..479266c0b 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 @@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Stream; - import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; import jakarta.servlet.Filter; @@ -57,10 +56,12 @@ import org.springframework.web.servlet.DispatcherServlet; /** - * Represents the main entry point into interaction with web application over light-weight proxy. - * After creating an instance via {@link #INSTANCE(Class...)} operation which will initialize the provided component - * classes of your web application (effectively starting your web application less web server), - * you use {@link #service(HttpServletRequest, HttpServletResponse)} operation to send request and receive a response. + * Represents the main entry point into interaction with web application over light-weight + * proxy. After creating an instance via {@link #INSTANCE(Class...)} operation which will + * initialize the provided component classes of your web application (effectively starting + * your web application less web server), you use + * {@link #service(HttpServletRequest, HttpServletResponse)} operation to send request and + * receive a response. * * @author Oleg Zhurakousky * @@ -68,7 +69,8 @@ public final class ServerlessMVC { /** - * Name of the property to specify application context initialization timeout. Default is 20 sec. + * Name of the property to specify application context initialization timeout. Default + * is 20 sec. */ public static String INIT_TIMEOUT = "contextInitTimeout"; @@ -122,8 +124,10 @@ private void initializeContextAsync(Class... componentClasses) { } private void initContext(Class... componentClasses) { - this.applicationContext = (ServletWebServerApplicationContext) SpringApplication.run(componentClasses, new String[] {}); - if (this.applicationContext.containsBean(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { + this.applicationContext = (ServletWebServerApplicationContext) SpringApplication.run(componentClasses, + new String[] {}); + if (this.applicationContext + .containsBean(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { this.dispatcher = this.applicationContext.getBean(DispatcherServlet.class); } } @@ -144,23 +148,25 @@ public void stop() { } /** - * Perform a request and return a type that allows chaining further actions, - * such as asserting expectations, on the result. - * - * @param requestBuilder used to prepare the request to execute; see static - * factory methods in - * {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders} + * Perform a request and return a type that allows chaining further actions, such as + * asserting expectations, on the result. + * @param requestBuilder used to prepare the request to execute; see static factory + * methods in + * {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders} * @return an instance of {@link ResultActions} (never {@code null}) * @see org.springframework.test.web.servlet.request.MockMvcRequestBuilders * @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.initializationTimeout + " milliseconds. " - + "If you need to increase it, please set " + INIT_TIMEOUT + " environment variable"); + 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); } - public void service(HttpServletRequest request, HttpServletResponse response, CountDownLatch latch) throws Exception { + public void service(HttpServletRequest request, HttpServletResponse response, CountDownLatch latch) + throws Exception { ProxyFilterChain filterChain = new ProxyFilterChain(this.dispatcher); filterChain.doFilter(request, response); @@ -208,16 +214,17 @@ private static class ProxyFilterChain implements FilterChain { @Nullable private Iterator iterator; - /** * Create a {@code FilterChain} with Filter's and a Servlet. - * * @param servlet the {@link Servlet} to invoke in this {@link FilterChain} * @since 4.0.x */ ProxyFilterChain(DispatcherServlet servlet) { List filters = new ArrayList<>(); - servlet.getServletContext().getFilterRegistrations().values().forEach(fr -> filters.add(((ServerlessFilterRegistration) fr).getFilter())); + servlet.getServletContext() + .getFilterRegistrations() + .values() + .forEach(fr -> filters.add(((ServerlessFilterRegistration) fr).getFilter())); Assert.notNull(filters, "filters cannot be null"); Assert.noNullElements(filters, "filters cannot contain null values"); this.filters = initFilterList(servlet, filters.toArray(new Filter[] {})); @@ -245,8 +252,8 @@ public ServletResponse getResponse() { } /** - * Invoke registered {@link Filter Filters} and/or {@link Servlet} also saving - * the request and response. + * Invoke registered {@link Filter Filters} and/or {@link Servlet} also saving the + * request and response. */ @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { @@ -288,10 +295,14 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha throws IOException, ServletException { try { - if (((HttpServletResponse) response).getStatus() != HttpStatus.OK.value() && request instanceof ServerlessHttpServletRequest) { - ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, ((HttpServletResponse) response).getStatus()); - this.setErrorMessageAttribute((ServerlessHttpServletRequest) request, (ServerlessHttpServletResponse) response, null); - ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, ((HttpServletRequest) request).getRequestURI()); + if (((HttpServletResponse) response).getStatus() != HttpStatus.OK.value() + && request instanceof ServerlessHttpServletRequest) { + ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, + ((HttpServletResponse) response).getStatus()); + this.setErrorMessageAttribute((ServerlessHttpServletRequest) request, + (ServerlessHttpServletResponse) response, null); + ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, + ((HttpServletRequest) request).getRequestURI()); ((ServerlessHttpServletRequest) request).setRequestURI("/error"); this.delegateServlet.service(request, response); @@ -302,10 +313,12 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha } catch (Exception e) { if (request instanceof ServerlessHttpServletRequest) { - ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpStatus.INTERNAL_SERVER_ERROR.value()); + ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, + HttpStatus.INTERNAL_SERVER_ERROR.value()); this.setErrorMessageAttribute((HttpServletRequest) request, (HttpServletResponse) response, e); ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, e); - ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, ((HttpServletRequest) request).getRequestURI()); + ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, + ((HttpServletRequest) request).getRequestURI()); ((ServerlessHttpServletRequest) request).setRequestURI("/error"); } @@ -315,15 +328,18 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha } } - private void setErrorMessageAttribute(HttpServletRequest request, HttpServletResponse response, Exception exception) { + private void setErrorMessageAttribute(HttpServletRequest request, HttpServletResponse response, + Exception exception) { if (exception != null && StringUtils.hasText(exception.getMessage())) { request.setAttribute(RequestDispatcher.ERROR_MESSAGE, exception.getMessage()); } - else if (response instanceof ServerlessHttpServletResponse proxyResponse && StringUtils.hasText(proxyResponse.getErrorMessage())) { + else if (response instanceof ServerlessHttpServletResponse proxyResponse + && StringUtils.hasText(proxyResponse.getErrorMessage())) { request.setAttribute(RequestDispatcher.ERROR_MESSAGE, proxyResponse.getErrorMessage()); } else { - request.setAttribute(RequestDispatcher.ERROR_MESSAGE, HttpStatus.valueOf(response.getStatus()).getReasonPhrase()); + request.setAttribute(RequestDispatcher.ERROR_MESSAGE, + HttpStatus.valueOf(response.getStatus()).getReasonPhrase()); } } @@ -340,7 +356,9 @@ public void destroy() { public String toString() { return this.delegateServlet.toString(); } + } + } public static class ProxyServletConfig implements ServletConfig { @@ -370,5 +388,7 @@ public Enumeration getInitParameterNames() { public String getInitParameter(String name) { return null; } + } + } 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 9f54ffecf..4257de3bd 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 @@ -31,7 +31,6 @@ import java.util.Map; import java.util.Set; - import jakarta.servlet.Filter; import jakarta.servlet.FilterRegistration; import jakarta.servlet.RequestDispatcher; @@ -50,8 +49,8 @@ /** * Stub representation of {@link ServletContext} to satisfy required dependencies to - * successfully proxy incoming web requests directly (serverlessely) to target web application. - * Most methods are not implemented. + * successfully proxy incoming web requests directly (serverlessely) to target web + * application. Most methods are not implemented. * * @author Oleg Zhurakousky * @@ -160,9 +159,8 @@ public String getRealPath(String path) { @Override public String getServerInfo() { - return ClassUtils.isPresent("com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler", ClassUtils.getDefaultClassLoader()) - ? "aws-serverless-java-container/6.0" - : "serverless-web-proxy"; + return ClassUtils.isPresent("com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler", + ClassUtils.getDefaultClassLoader()) ? "aws-serverless-java-container/6.0" : "serverless-web-proxy"; } @Override @@ -366,4 +364,5 @@ public String getResponseCharacterEncoding() { public void setResponseCharacterEncoding(String encoding) { throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessServletRegistration.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessServletRegistration.java index 99e2e30e2..e787a5444 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessServletRegistration.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessServletRegistration.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package org.springframework.cloud.function.serverless.web; import java.util.Collection; @@ -28,12 +27,12 @@ import jakarta.servlet.ServletSecurityElement; /** - * * @author Oleg Zhurakousky * @since 4.x * */ -public class ServerlessServletRegistration implements ServletRegistration, ServletRegistration.Dynamic, Comparable { +public class ServerlessServletRegistration + implements ServletRegistration, ServletRegistration.Dynamic, Comparable { private final String servletName; 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 741336d9a..96d80260c 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 @@ -68,7 +68,6 @@ import org.springframework.web.context.ConfigurableWebApplicationContext; /** - * * @author Oleg Zhurakousky * */ @@ -119,11 +118,10 @@ public ConfigurableWebApplicationContext run(String... args) { throw new IllegalStateException(ex); } - //throw new AbandonedRunException(); + // throw new AbandonedRunException(); return context; } - private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ConfigurableBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // Create and configure the environment @@ -150,28 +148,31 @@ private ConfigurableEnvironment getOrCreateEnvironment() { private SpringApplicationRunListeners getRunListeners(String[] args) { ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this); argumentResolver = argumentResolver.and(String[].class, args); - List listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class, argumentResolver); + List listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class, + argumentResolver); return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup); } 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(Locale.ROOT)) - : Banner.Mode.CONSOLE; + Banner.Mode bannerMode = environment.containsProperty("spring.main.banner-mode") + ? Banner.Mode + .valueOf(environment.getProperty("spring.main.banner-mode").trim().toUpperCase(Locale.ROOT)) + : Banner.Mode.CONSOLE; if (bannerMode == Banner.Mode.OFF) { return null; } - SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, new SpringAwsBanner()); + SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, + new SpringAwsBanner()); return bannerPrinter.print(environment, this.getMainApplicationClass(), System.out); } - private DefaultBootstrapContext createBootstrapContext() { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); - ArrayList bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); + ArrayList bootstrapRegistryInitializers = new ArrayList<>( + getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); return bootstrapContext; } @@ -227,7 +228,8 @@ private void addAotGeneratedInitializerIfNecessary(List> aotInitializers = new ArrayList<>( initializers.stream().filter(AotApplicationContextInitializer.class::isInstance).toList()); if (aotInitializers.isEmpty()) { - String initializerClassName = this.getMainApplicationClass().getName() + "__ApplicationContextInitializer"; + String initializerClassName = this.getMainApplicationClass().getName() + + "__ApplicationContextInitializer"; aotInitializers.add(AotApplicationContextInitializer.forInitializerClasses(initializerClassName)); } initializers.removeAll(aotInitializers); @@ -325,6 +327,7 @@ public void printBanner(Environment environment, Class sourceClass, PrintStre } } + } private static class SpringAwsBanner implements Banner { @@ -388,12 +391,14 @@ void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, Configur } void contextPrepared(ConfigurableApplicationContext context) { - doWithListeners("spring.boot.application.context-prepared", (listener) -> listener.contextPrepared(context)); + doWithListeners("spring.boot.application.context-prepared", + (listener) -> listener.contextPrepared(context)); } void contextLoaded(ConfigurableApplicationContext context) { doWithListeners("spring.boot.application.context-loaded", (listener) -> listener.contextLoaded(context)); } + private void doWithListeners(String stepName, Consumer listenerAction) { doWithListeners(stepName, listenerAction, null); } @@ -407,5 +412,7 @@ private void doWithListeners(String stepName, Consumer breeds = new ArrayList<>(); private PetData() { @@ -110,8 +110,8 @@ public static String getRandomName() { public static Date getRandomDoB() { GregorianCalendar gc = new GregorianCalendar(); - int year = ThreadLocalRandom.current().nextInt(Calendar.getInstance().get(Calendar.YEAR) - 15, - Calendar.getInstance().get(Calendar.YEAR)); + int year = ThreadLocalRandom.current() + .nextInt(Calendar.getInstance().get(Calendar.YEAR) - 15, Calendar.getInstance().get(Calendar.YEAR)); gc.set(Calendar.YEAR, year); @@ -120,4 +120,5 @@ public static Date getRandomDoB() { gc.set(Calendar.DAY_OF_YEAR, dayOfYear); return gc.getTime(); } + } 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 90405243a..6e4ff49dd 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 @@ -51,7 +51,6 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - @Configuration @Import({ PetsController.class, FreemarkerController.class }) @EnableWebSecurity @@ -59,8 +58,8 @@ public class PetStoreSpringAppConfig { /* - * Create required HandlerMapping, to avoid several default HandlerMapping - * instances being created + * Create required HandlerMapping, to avoid several default HandlerMapping instances + * being created */ @Bean public HandlerMapping handlerMapping() { @@ -68,8 +67,8 @@ public HandlerMapping handlerMapping() { } /* - * Create required HandlerAdapter, to avoid several default HandlerAdapter - * instances being created + * Create required HandlerAdapter, to avoid several default HandlerAdapter instances + * being created */ @Bean public HandlerAdapter handlerAdapter() { @@ -79,10 +78,7 @@ public HandlerAdapter handlerAdapter() { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, SimpleFilter simpleFilter, AnotherFilter anotherFilter) throws Exception { - http - .csrf(csrf -> csrf.disable()) - .cors(cors -> cors.disable()) - .addFilterBefore(new GenericFilterBean() { + http.csrf(csrf -> csrf.disable()).cors(cors -> cors.disable()).addFilterBefore(new GenericFilterBean() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @@ -93,14 +89,12 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext); chain.doFilter(request, response); } - }, SecurityContextHolderFilter.class) - .securityMatcher("/foo/deny") - .authorizeHttpRequests(auth -> { + }, SecurityContextHolderFilter.class).securityMatcher("/foo/deny").authorizeHttpRequests(auth -> { auth.anyRequest().hasRole("FOO"); }) - .addFilterAfter(simpleFilter, LogoutFilter.class) - .addFilterAfter(anotherFilter, RequestCacheAwareFilter.class) - .exceptionHandling(f -> f.accessDeniedHandler(new MyAccessDeinedHandler())); + .addFilterAfter(simpleFilter, LogoutFilter.class) + .addFilterAfter(anotherFilter, RequestCacheAwareFilter.class) + .exceptionHandling(f -> f.accessDeniedHandler(new MyAccessDeinedHandler())); return http.build(); } @@ -129,6 +123,7 @@ public AnotherFilter anotherFilter() { } public static class SimpleFilter extends OncePerRequestFilter { + /** * */ @@ -146,6 +141,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse filterChain.doFilter(request, response); } + } public static class AnotherFilter extends OncePerRequestFilter { @@ -154,6 +150,7 @@ public static class AnotherFilter extends OncePerRequestFilter { * */ public boolean invoked; + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { @@ -165,6 +162,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } filterChain.doFilter(request, response); } + } public static class MyAccessDeinedHandler implements AccessDeniedHandler { @@ -176,4 +174,5 @@ public void handle(HttpServletRequest request, HttpServletResponse response, } } + } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetsController.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetsController.java index e71a09599..a1fcc467d 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetsController.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetsController.java @@ -32,13 +32,12 @@ import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.servlet.config.annotation.EnableWebMvc; - @RestController @EnableWebMvc public class PetsController { @RequestMapping(path = "/petsAsync/", method = RequestMethod.POST) - public DeferredResult createPetAsync(@RequestBody Pet newPet) { + public DeferredResult createPetAsync(@RequestBody Pet newPet) { if (newPet.getName() == null || newPet.getBreed() == null) { return null; } @@ -107,6 +106,9 @@ public Pet foo() { @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such Dog") // 404 public class DogNotFoundException extends RuntimeException { + // ... + } + } 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 8dabd2737..2bce1db85 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 @@ -72,12 +72,16 @@ else if (function.isConsumer()) { return allFunctions; } - private String toSimplePolyOut(FunctionInvocationWrapper function) { - return FunctionTypeUtils.getRawType(function.getItemType(function.getOutputType())).getSimpleName().toLowerCase(Locale.ROOT); + 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(Locale.ROOT); + 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/CloudEventHeaderEnricher.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventHeaderEnricher.java index 63c9e84b5..0fcbd261d 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventHeaderEnricher.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventHeaderEnricher.java @@ -20,11 +20,12 @@ * Strategy that should be implemented by the user to help with outgoing Cloud Event * headers.
*
- * NOTE: The provided instance of {@link CloudEventMessageBuilder} may or may not be initialized - * with default values, so it is the responsibility of the user to ensure that all required Cloud Events - * attributes are set. That said, Spring frameworks which utilize this interface - * will ensure that the provided {@link CloudEventMessageBuilder} is initialized with default values, leaving - * you responsible to only set the attributes you need.
+ * NOTE: The provided instance of {@link CloudEventMessageBuilder} may or may not be + * initialized with default values, so it is the responsibility of the user to ensure that + * all required Cloud Events attributes are set. That said, Spring frameworks which + * utilize this interface will ensure that the provided {@link CloudEventMessageBuilder} + * is initialized with default values, leaving you responsible to only set the attributes + * you need.
* Once implemented, simply configure it as a bean and the framework will invoke it before * the outbound Cloud Event Message is finalized. * diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageBuilder.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageBuilder.java index 5fee1ff7f..f7f7f4931 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageBuilder.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageBuilder.java @@ -34,9 +34,8 @@ import org.springframework.util.StringUtils; /** - * Message builder which is aware of Cloud Event semantics. - * It provides type-safe setters for v1.0 Cloud Event attributes while - * supporting all other versions via convenient + * Message builder which is aware of Cloud Event semantics. It provides type-safe setters + * for v1.0 Cloud Event attributes while supporting all other versions via convenient * {@link #setHeader(String, Object)} method. * * @author Oleg Zhurakousky @@ -134,7 +133,6 @@ public CloudEventMessageBuilder copyHeaders(Map headers) { /** * Returns a snapshot of the headers {@link Map} at the time this method is called. * The returned Map is read-only. - * * @return map of headers */ public Map toHeadersMap() { @@ -142,9 +140,9 @@ public Map toHeadersMap() { } /** - * Will build the message ensuring that the Cloud Event attributes are all - * prefixed with the prefix determined by the framework. If you want to - * use a specific prefix please use {@link #build(String)} method. + * Will build the message ensuring that the Cloud Event attributes are all prefixed + * with the prefix determined by the framework. If you want to use a specific prefix + * please use {@link #build(String)} method. * @return instance of {@link Message} */ public Message build() { @@ -152,20 +150,19 @@ public Message build() { } /** - * Will build the message ensuring that the Cloud Event attributes are - * prefixed with the 'attributePrefixToUse'. - * + * Will build the message ensuring that the Cloud Event attributes are prefixed with + * the 'attributePrefixToUse'. * @param attributePrefixToUse prefix to use for attributes * @return instance of {@link Message} */ public Message build(String attributePrefixToUse) { - Assert.isTrue(attributePrefixToUse.equals(CloudEventMessageUtils.DEFAULT_ATTR_PREFIX) - || attributePrefixToUse.equals(CloudEventMessageUtils.KAFKA_ATTR_PREFIX) - || attributePrefixToUse.equals(CloudEventMessageUtils.AMQP_ATTR_PREFIX), "Supported prefixes are " - + CloudEventMessageUtils.DEFAULT_ATTR_PREFIX - + ", " + CloudEventMessageUtils.KAFKA_ATTR_PREFIX - + ", " + CloudEventMessageUtils.AMQP_ATTR_PREFIX - + ". Was " + attributePrefixToUse); + Assert.isTrue( + attributePrefixToUse.equals(CloudEventMessageUtils.DEFAULT_ATTR_PREFIX) + || attributePrefixToUse.equals(CloudEventMessageUtils.KAFKA_ATTR_PREFIX) + || attributePrefixToUse.equals(CloudEventMessageUtils.AMQP_ATTR_PREFIX), + "Supported prefixes are " + CloudEventMessageUtils.DEFAULT_ATTR_PREFIX + ", " + + CloudEventMessageUtils.KAFKA_ATTR_PREFIX + ", " + CloudEventMessageUtils.AMQP_ATTR_PREFIX + + ". Was " + attributePrefixToUse); if (StringUtils.hasText(attributePrefixToUse)) { String[] keys = this.headers.keySet().toArray(new String[] {}); for (String key : keys) { @@ -206,8 +203,10 @@ private Message doBuild(String prefix) { } MessageHeaders headers = new MessageHeaders(this.headers); GenericMessage message = new GenericMessage(this.data, headers); - Assert.isTrue(CloudEventMessageUtils.isCloudEvent(message), "The message does not appear to be a valid Cloud Event, " - + "since one of the required attributes (id, specversion, type, source) is missing"); + Assert.isTrue(CloudEventMessageUtils.isCloudEvent(message), + "The message does not appear to be a valid Cloud Event, " + + "since one of the required attributes (id, specversion, type, source) is missing"); return message; } + } 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 2db8c1fc9..41fe83f5a 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 @@ -56,7 +56,8 @@ public final class CloudEventMessageUtils { @Override public MimeType resolve(@Nullable MessageHeaders headers) { - if (headers.containsKey("content-type")) { // this is temporary workaround for RSocket + if (headers.containsKey("content-type")) { // this is temporary workaround for + // RSocket return MimeType.valueOf(headers.get("content-type").toString()); } return super.resolve(headers); @@ -67,7 +68,7 @@ public MimeType resolve(@Nullable MessageHeaders headers) { private CloudEventMessageUtils() { } - //=========== INTERNAL USE ONLY == + // =========== INTERNAL USE ONLY == static String _DATA = "data"; static String _ID = "id"; @@ -87,6 +88,7 @@ private CloudEventMessageUtils() { static String _SUBJECT = "subject"; static String _TIME = "time"; + // ================================ /** @@ -169,7 +171,6 @@ private CloudEventMessageUtils() { */ public static String TIME = DEFAULT_ATTR_PREFIX + _TIME; - public static String getId(Message message) { String prefix = determinePrefixToUse(message.getHeaders()); Object value = message.getHeaders().get(prefix + MessageHeaders.ID); @@ -237,17 +238,18 @@ public static T getData(Message message) { } public static Map getAttributes(Message message) { - return message.getHeaders().entrySet().stream() - .filter(e -> isAttribute(e.getKey())) - .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); + return message.getHeaders() + .entrySet() + .stream() + .filter(e -> isAttribute(e.getKey())) + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); } /** - * This method does several things. - * First it canonicalizes Cloud Events attributes ensuring that they are all prefixed - * with 'ce-' prefix regardless where they came from. - * It also transforms structured-mode Cloud Event to binary-mode and then canonicalizes attributes - * as well as described in the previous sentence. + * This method does several things. First it canonicalizes Cloud Events attributes + * ensuring that they are all prefixed with 'ce-' prefix regardless where they came + * from. It also transforms structured-mode Cloud Event to binary-mode and then + * canonicalizes attributes as well as described in the previous sentence. */ @SuppressWarnings("unchecked") static Message toCanonical(Message inputMessage, MessageConverter messageConverter) { @@ -256,7 +258,9 @@ static Message toCanonical(Message inputMessage, MessageConverter messageC boolean isCloudEvent = isCloudEvent(inputMessage); if (isCloudEvent && headers.containsKey("content-type")) { - inputMessage = MessageBuilder.fromMessage(inputMessage).setHeader(MessageHeaders.CONTENT_TYPE, headers.get("content-type")).build(); + inputMessage = MessageBuilder.fromMessage(inputMessage) + .setHeader(MessageHeaders.CONTENT_TYPE, headers.get("content-type")) + .build(); } MimeType contentType = contentTypeResolver.resolve(inputMessage.getHeaders()); String inputContentType = (String) inputMessage.getHeaders().get(DATACONTENTTYPE); @@ -264,29 +268,31 @@ static Message toCanonical(Message inputMessage, MessageConverter messageC if (!isCloudEvent && contentType != null) { // structured-mode - if (contentType.getType().equals(APPLICATION_CLOUDEVENTS.getType()) && contentType - .getSubtype().startsWith(APPLICATION_CLOUDEVENTS.getSubtype())) { + if (contentType.getType().equals(APPLICATION_CLOUDEVENTS.getType()) + && contentType.getSubtype().startsWith(APPLICATION_CLOUDEVENTS.getSubtype())) { String dataContentType = StringUtils.hasText(inputContentType) ? inputContentType : MimeTypeUtils.APPLICATION_JSON_VALUE; String suffix = contentType.getSubtypeSuffix() == null ? "json" : contentType.getSubtypeSuffix(); MimeType cloudEventDeserializationContentType = MimeTypeUtils - .parseMimeType(contentType.getType() + "/" + suffix); + .parseMimeType(contentType.getType() + "/" + suffix); Message cloudEventMessage = MessageBuilder.fromMessage(inputMessage) - .setHeader(MessageHeaders.CONTENT_TYPE, cloudEventDeserializationContentType) - .setHeader(DATACONTENTTYPE, dataContentType).build(); + .setHeader(MessageHeaders.CONTENT_TYPE, cloudEventDeserializationContentType) + .setHeader(DATACONTENTTYPE, dataContentType) + .build(); Map structuredCloudEvent = (Map) messageConverter - .fromMessage(cloudEventMessage, Map.class); + .fromMessage(cloudEventMessage, Map.class); canonicalizeHeaders(structuredCloudEvent, true); - return buildBinaryMessageFromStructuredMap(structuredCloudEvent, - inputMessage.getHeaders()); + return buildBinaryMessageFromStructuredMap(structuredCloudEvent, inputMessage.getHeaders()); } } else if (StringUtils.hasText(inputContentType)) { - // binary-mode, but DATACONTENTTYPE was specified explicitly so we set it as CT to ensure proper message converters are used. - return MessageBuilder.fromMessage(inputMessage).setHeader(MessageHeaders.CONTENT_TYPE, inputContentType) - .build(); + // binary-mode, but DATACONTENTTYPE was specified explicitly so we set it as + // CT to ensure proper message converters are used. + return MessageBuilder.fromMessage(inputMessage) + .setHeader(MessageHeaders.CONTENT_TYPE, inputContentType) + .build(); } return inputMessage; } @@ -294,32 +300,29 @@ else if (StringUtils.hasText(inputContentType)) { /** * Attempts to {@link #canonicalizeHeaders canonicalize} the headers of a message. * @param message the message - * @return a copy of the message with the canonicalized headers or the passed in unmodified message if no - * headers were canonicalized + * @return a copy of the message with the canonicalized headers or the passed in + * unmodified message if no headers were canonicalized */ // VisibleForTesting static Message canonicalizeHeadersWithPossibleCopy(Message message) { Map headers = new HashMap<>(message.getHeaders()); boolean headersModified = canonicalizeHeaders(headers, false); if (headersModified) { - message = MessageBuilder.fromMessage(message) - .removeHeaders("*") - .copyHeaders(headers) - .build(); + message = MessageBuilder.fromMessage(message).removeHeaders("*").copyHeaders(headers).build(); } return message; } /** - * Will canonicalize Cloud Event attributes (headers) by ensuring canonical - * prefix for all attributes and extensions regardless of where they came from. - * The canonical prefix is 'ce-'. + * Will canonicalize Cloud Event attributes (headers) by ensuring canonical prefix for + * all attributes and extensions regardless of where they came from. The canonical + * prefix is 'ce-'. * * So, for example 'ce_source' will become 'ce-source'. * @param headers message headers - * @param structured boolean signifying that headers map represents structured Cloud Event - * at which point attributes without any prefix will still be treated as - * Cloud Event attributes. + * @param structured boolean signifying that headers map represents structured Cloud + * Event at which point attributes without any prefix will still be treated as Cloud + * Event attributes. * @return whether the headers were modified during the process */ private static boolean canonicalizeHeaders(Map headers, boolean structured) { @@ -379,7 +382,8 @@ else if (key.startsWith("amqp")) { static String determinePrefixToUse(Map messageHeaders, boolean strict) { String targetProtocol = extractTargetProtocol(messageHeaders); String prefix = determinePrefixToUse(targetProtocol); - if (StringUtils.hasText(prefix) && (strict || StringUtils.hasText((String) messageHeaders.get(prefix + _SPECVERSION)))) { + if (StringUtils.hasText(prefix) + && (strict || StringUtils.hasText((String) messageHeaders.get(prefix + _SPECVERSION)))) { return prefix; } else { @@ -426,26 +430,23 @@ else if (Protocols.HTTP.equals(targetProtocol)) { * @return true if this Message represents Cloud Event in binary-mode */ public static boolean isCloudEvent(Message message) { - MessageStructureWithCaseInsensitiveHeaderKeys _message = MessageUtils.toCaseInsensitiveHeadersStructure(message); - return (_message.getHeaders().containsKey(SPECVERSION) - && _message.getHeaders().containsKey(TYPE) - && _message.getHeaders().containsKey(SOURCE)) - || - (_message.getHeaders().containsKey(_SPECVERSION) - && _message.getHeaders().containsKey(_TYPE) + MessageStructureWithCaseInsensitiveHeaderKeys _message = MessageUtils + .toCaseInsensitiveHeadersStructure(message); + return (_message.getHeaders().containsKey(SPECVERSION) && _message.getHeaders().containsKey(TYPE) + && _message.getHeaders().containsKey(SOURCE)) + || (_message.getHeaders().containsKey(_SPECVERSION) && _message.getHeaders().containsKey(_TYPE) && _message.getHeaders().containsKey(_SOURCE)) - || - (_message.getHeaders().containsKey(AMQP_ATTR_PREFIX + _SPECVERSION) - && _message.getHeaders().containsKey(AMQP_ATTR_PREFIX + _TYPE) - && _message.getHeaders().containsKey(AMQP_ATTR_PREFIX + _SOURCE)) - || - (_message.getHeaders().containsKey(KAFKA_ATTR_PREFIX + _SPECVERSION) - && _message.getHeaders().containsKey(KAFKA_ATTR_PREFIX + _TYPE) - && _message.getHeaders().containsKey(KAFKA_ATTR_PREFIX + _SOURCE)); + || (_message.getHeaders().containsKey(AMQP_ATTR_PREFIX + _SPECVERSION) + && _message.getHeaders().containsKey(AMQP_ATTR_PREFIX + _TYPE) + && _message.getHeaders().containsKey(AMQP_ATTR_PREFIX + _SOURCE)) + || (_message.getHeaders().containsKey(KAFKA_ATTR_PREFIX + _SPECVERSION) + && _message.getHeaders().containsKey(KAFKA_ATTR_PREFIX + _TYPE) + && _message.getHeaders().containsKey(KAFKA_ATTR_PREFIX + _SOURCE)); } private static boolean isAttribute(String key) { - return key.startsWith(DEFAULT_ATTR_PREFIX) || key.startsWith(AMQP_ATTR_PREFIX) || key.startsWith(KAFKA_ATTR_PREFIX); + return key.startsWith(DEFAULT_ATTR_PREFIX) || key.startsWith(AMQP_ATTR_PREFIX) + || key.startsWith(KAFKA_ATTR_PREFIX); } private static Message buildBinaryMessageFromStructuredMap(Map structuredCloudEvent, @@ -455,9 +456,8 @@ private static Message buildBinaryMessageFromStructuredMap(Map messageBuilder = CloudEventMessageBuilder - .withData(payload) - .copyHeaders(structuredCloudEvent); + CloudEventMessageBuilder messageBuilder = CloudEventMessageBuilder.withData(payload) + .copyHeaders(structuredCloudEvent); for (String key : originalHeaders.keySet()) { if (!MessageHeaders.ID.equals(key)) { @@ -486,11 +486,13 @@ private static String toString(byte[] value) { } public static class Protocols { + static String AMQP = "amqp"; static String AVRO = "avro"; static String HTTP = "http"; static String JSON = "json"; static String KAFKA = "kafka"; + } } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionExtensionConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionExtensionConfiguration.java index 8e64cb789..aafe37432 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionExtensionConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionExtensionConfiguration.java @@ -32,11 +32,14 @@ @Configuration(proxyBeanMethods = false) public class CloudEventsFunctionExtensionConfiguration { - // The following two beans are intended to be mutually exclusive. Only one should be activated based + // The following two beans are intended to be mutually exclusive. Only one should be + // activated based // on the presence of Cloud Event SDK API @Bean @ConditionalOnMissingBean - public FunctionInvocationHelper> nativeFunctionInvocationHelper(@Nullable CloudEventHeaderEnricher cloudEventHeadersProvider) { + public FunctionInvocationHelper> nativeFunctionInvocationHelper( + @Nullable CloudEventHeaderEnricher cloudEventHeadersProvider) { return new CloudEventsFunctionInvocationHelper(cloudEventHeadersProvider); } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionInvocationHelper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionInvocationHelper.java index f50ae60b5..450f91ec3 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionInvocationHelper.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionInvocationHelper.java @@ -38,14 +38,15 @@ import org.springframework.util.StringUtils; /** - * Implementation of {@link FunctionInvocationHelper} to support Cloud Events. - * This is a primary (and the only) integration bridge with {@link FunctionInvocationWrapper}. + * Implementation of {@link FunctionInvocationHelper} to support Cloud Events. This is a + * primary (and the only) integration bridge with {@link FunctionInvocationWrapper}. * * @author Oleg Zhurakousky * @since 3.1 * */ -public class CloudEventsFunctionInvocationHelper implements FunctionInvocationHelper>, ApplicationContextAware { +public class CloudEventsFunctionInvocationHelper + implements FunctionInvocationHelper>, ApplicationContextAware { private Log logger = LogFactory.getLog(this.getClass()); @@ -71,13 +72,15 @@ public class CloudEventsFunctionInvocationHelper implements FunctionInvocationHe @Override public boolean isRetainOutputAsMessage(Message message) { - return message.getHeaders().containsKey("partitionKey") || (message.getHeaders().containsKey(MessageUtils.MESSAGE_TYPE) - && message.getHeaders().get(MessageUtils.MESSAGE_TYPE).equals(CloudEventMessageUtils.CLOUDEVENT_VALUE)); + return message.getHeaders().containsKey("partitionKey") || (message.getHeaders() + .containsKey(MessageUtils.MESSAGE_TYPE) + && message.getHeaders().get(MessageUtils.MESSAGE_TYPE).equals(CloudEventMessageUtils.CLOUDEVENT_VALUE)); } @Override public Message preProcessInput(Message input, Object inputConverter) { - // TODO find a way to invoke it conditionally. May be check for certain headers with all known prefixes as well as content type + // TODO find a way to invoke it conditionally. May be check for certain headers + // with all known prefixes as well as content type try { return CloudEventMessageUtils.toCanonical(input, (MessageConverter) inputConverter); } @@ -93,7 +96,8 @@ public void setMessageConverter(CompositeMessageConverter messageConverter) { @Override public Message postProcessResult(Object result, Message input) { Object convertedResult = result; - if (this.messageConverter != null && CLOUD_EVENT_CLASS != null && CLOUD_EVENT_CLASS.isAssignableFrom(result.getClass())) { + if (this.messageConverter != null && CLOUD_EVENT_CLASS != null + && CLOUD_EVENT_CLASS.isAssignableFrom(result.getClass())) { convertedResult = this.messageConverter.toMessage(result, input.getHeaders()); } @@ -105,9 +109,10 @@ else if (result instanceof Message resultMessage) { targetPrefix = CloudEventMessageUtils.determinePrefixToUse(resultMessage.getHeaders(), true); } - Assert.hasText(targetPrefix, "Unable to determine prefix for Cloud Event atttributes, " - + "which they must have according to protocol specification. Consider adding 'target-protocol' " - + "header with values of one of the supported protocols - [kafka, amqp, http]"); + Assert.hasText(targetPrefix, + "Unable to determine prefix for Cloud Event atttributes, " + + "which they must have according to protocol specification. Consider adding 'target-protocol' " + + "header with values of one of the supported protocols - [kafka, amqp, http]"); if (logger.isDebugEnabled()) { logger.debug("Cloud event attributes will be prefixed with '" + targetPrefix + "'"); } @@ -120,7 +125,8 @@ public void setApplicationContext(ApplicationContext applicationContext) throws } private Message doPostProcessResult(Object result, String targetPrefix) { - Message resultMessage = null; //result instanceof Message ? (Message) result : null; + Message resultMessage = null; // result instanceof Message ? (Message) + // result : null; CloudEventMessageBuilder messageBuilder; if (result instanceof Message) { if (CloudEventMessageUtils.isCloudEvent((Message) result)) { @@ -131,11 +137,10 @@ private Message doPostProcessResult(Object result, String targetPrefix) { } } else { - messageBuilder = CloudEventMessageBuilder - .withData(result) - .setId(UUID.randomUUID().toString()) - .setSource(URI.create("http://spring.io/" + getApplicationName())) - .setType(result.getClass().getName()); + messageBuilder = CloudEventMessageBuilder.withData(result) + .setId(UUID.randomUUID().toString()) + .setSource(URI.create("http://spring.io/" + getApplicationName())) + .setType(result.getClass().getName()); } if (this.cloudEventAttributesProvider != null) { @@ -154,4 +159,5 @@ private String getApplicationName() { String name = environment.getProperty("spring.application.name"); return (StringUtils.hasText(name) ? name : ""); } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/DefaultMessageRoutingHandler.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/DefaultMessageRoutingHandler.java index e2788cbb7..73e0e6e7a 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/DefaultMessageRoutingHandler.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/DefaultMessageRoutingHandler.java @@ -25,11 +25,11 @@ import org.springframework.messaging.Message; /** - * Strategy for implementing a handler for un-routable messages. - * Works in parallel with {@link RoutingFunction}. When registered as a bean, RoutingFunction will not throw - * an exception if it can not route message and instead such message will be routed to this function. - * Its default implementation simply logs the un-routable event. - * Users are encouraged to provide their own implementation of this class. + * Strategy for implementing a handler for un-routable messages. Works in parallel with + * {@link RoutingFunction}. When registered as a bean, RoutingFunction will not throw an + * exception if it can not route message and instead such message will be routed to this + * function. Its default implementation simply logs the un-routable event. Users are + * encouraged to provide their own implementation of this class. * * @author Oleg Zhurakousky * @since 3.2.9 @@ -42,10 +42,12 @@ public class DefaultMessageRoutingHandler implements Consumer> { @Override public void accept(Message message) { if (logger.isDebugEnabled()) { - logger.debug("Route-to function can not be located in FunctionCatalog. Dropping unroutable message: " + message + ""); + logger.debug("Route-to function can not be located in FunctionCatalog. Dropping unroutable message: " + + message + ""); } else { logger.warn("Route-to function can not be located in FunctionCatalog. Droping message"); } } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionCatalog.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionCatalog.java index 21aab675c..4508eac69 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionCatalog.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionCatalog.java @@ -21,7 +21,6 @@ import java.util.function.Function; import java.util.function.Supplier; - /** * @author Dave Syer * @author Oleg Zhurakousky @@ -30,9 +29,8 @@ public interface FunctionCatalog { /** * Will look up the instance of the functional interface by name only. - * - * @param functionDefinition the definition of the functional interface. Must - * not be null; + * @param functionDefinition the definition of the functional interface. Must not be + * null; * @return instance of the functional interface registered with this catalog */ default T lookup(String functionDefinition) { @@ -40,44 +38,41 @@ default T lookup(String functionDefinition) { } /** - * Will look up the instance of the functional interface by name and type which - * can only be Supplier, Consumer or Function. If type is not provided, the - * lookup will be made based on name only. - * - * @param instance type - * @param type the type of functional interface. Can be null - * @param functionDefinition the definition of the functional interface. Must - * not be null; + * Will look up the instance of the functional interface by name and type which can + * only be Supplier, Consumer or Function. If type is not provided, the lookup will be + * made based on name only. + * @param instance type + * @param type the type of functional interface. Can be null + * @param functionDefinition the definition of the functional interface. Must not be + * null; * @return instance of the functional interface registered with this catalog */ default T lookup(Class type, String functionDefinition) { return this.lookup(type, functionDefinition, (String[]) null); } - /** - * Will look up the instance of the functional interface by name only. - * This lookup method assumes a very specific semantics which are: function sub-type(s) + * Will look up the instance of the functional interface by name only. This lookup + * method assumes a very specific semantics which are: function sub-type(s) * expected to be {@code Message}.
- * For example, - *

- * {@code Function, Message>} or + * For example,
*
- * {@code Function>, Flux>>} or + * {@code Function, Message>} or
+ * {@code Function>, Flux>>} or
+ * {@code Consumer>>} etc. . .
*
- * {@code Consumer>>} etc. . . - *

- * The {@code acceptedOutputMimeTypes} are the string representation of {@link MimeType} where each - * mime-type in the provided array would correspond to the output with the same index - * (for cases of functions with multiple outputs) and is used to convert such output back - * to {@code Message}. - * If you need to provide several accepted types per specific output you can simply delimit - * them with comma (e.g., {@code application/json,text/plain...}). - * - * @param instance type which should be one of {@link Supplier}, {@link Function} or {@link Consumer}. - * @param functionDefinition the definition of a function (e.g., 'foo' or 'foo|bar') - * @param acceptedOutputMimeTypes acceptedOutputMimeTypes array of string representation of {@link MimeType}s - * used to convert function output back to {@code Message}. + * The {@code acceptedOutputMimeTypes} are the string representation of + * {@link MimeType} where each mime-type in the provided array would correspond to the + * output with the same index (for cases of functions with multiple outputs) and is + * used to convert such output back to {@code Message}. If you need to provide + * several accepted types per specific output you can simply delimit them with comma + * (e.g., {@code application/json,text/plain...}). + * @param instance type which should be one of {@link Supplier}, {@link Function} + * or {@link Consumer}. + * @param functionDefinition the definition of a function (e.g., 'foo' or 'foo|bar') + * @param acceptedOutputMimeTypes acceptedOutputMimeTypes array of string + * representation of {@link MimeType}s used to convert function output back to + * {@code Message}. * @return instance of the functional interface registered with this catalog */ default T lookup(String functionDefinition, String... expectedOutputMimeTypes) { @@ -90,7 +85,6 @@ default T lookup(String functionDefinition, String... expectedOutputMimeType /** * Return the count of functions registered in this catalog. - * * @return the count of functions registered in this catalog */ default int size() { diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionProperties.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionProperties.java index df4642edd..c115a9559 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionProperties.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionProperties.java @@ -32,7 +32,6 @@ import org.springframework.util.CollectionUtils; /** - * * @author Oleg Zhurakousky * @since 3.0 * @@ -46,7 +45,8 @@ public class FunctionProperties implements EnvironmentAware, ApplicationContextA public final static String PREFIX = "spring.cloud.function"; /** - * Name of the header to be used to instruct function to apply this content type for output conversion. + * Name of the header to be used to instruct function to apply this content type for + * output conversion. */ public final static String EXPECT_CONTENT_TYPE_HEADER = "expected-content-type"; @@ -66,14 +66,15 @@ public class FunctionProperties implements EnvironmentAware, ApplicationContextA public final static String PROXY = "proxy"; /** - * Definition of the function to be used. This could be function name (e.g., 'myFunction') - * or function composition definition (e.g., 'myFunction|yourFunction') + * Definition of the function to be used. This could be function name (e.g., + * 'myFunction') or function composition definition (e.g., 'myFunction|yourFunction') */ private String definition; /** - * SpEL expression which should result in function definition (e.g., function name or composition instruction). - * NOTE: SpEL evaluation context's root object is the input argument (e.g., Message). + * SpEL expression which should result in function definition (e.g., function name or + * composition instruction). NOTE: SpEL evaluation context's root object is the input + * argument (e.g., Message). */ private String routingExpression; @@ -92,18 +93,11 @@ public class FunctionProperties implements EnvironmentAware, ApplicationContextA public FunctionProperties() { ineligibleDefinitions = new ArrayList<>(); - String[] definitions = new String[] { - "org.springframework.boot", + String[] definitions = new String[] { "org.springframework.boot", "org.springframework.cloud.function.cloudevent.CloudEventsFunctionExtensionConfiguration", "org.springframework.cloud.function.context.config.FunctionsEndpointAutoConfiguration", - "classLoaderMetrics", - "jvmMemoryMetrics", - "jvmInfoMetrics", - "jvmCompilationMetrics", - "uptimeMetrics", - "kotlinToFunctionTransformer", - "CloudEventsMessageConverterConfiguration" - }; + "classLoaderMetrics", "jvmMemoryMetrics", "jvmInfoMetrics", "jvmCompilationMetrics", "uptimeMetrics", + "kotlinToFunctionTransformer", "CloudEventsMessageConverterConfiguration" }; ineligibleDefinitions.addAll(Arrays.asList(definitions)); } @@ -114,12 +108,15 @@ public Map getConfiguration() { @SuppressWarnings({ "unchecked", "rawtypes" }) public void setConfiguration(Map configuration) { for (Entry entry : configuration.entrySet()) { - String propertyX = "spring.cloud.function.configuration." + entry.getKey() + ".input-header-mapping-expression."; - String propertyY = "spring.cloud.function.configuration." + entry.getKey() + ".inputHeaderMappingExpression."; - Map headerMapping = entry.getValue().getInputHeaderMappingExpression(); + String propertyX = "spring.cloud.function.configuration." + entry.getKey() + + ".input-header-mapping-expression."; + String propertyY = "spring.cloud.function.configuration." + entry.getKey() + + ".inputHeaderMappingExpression."; + Map headerMapping = entry.getValue().getInputHeaderMappingExpression(); if (!CollectionUtils.isEmpty(headerMapping)) { for (Object k : headerMapping.keySet()) { - if (this.environment.containsProperty(propertyX + k) || this.environment.containsProperty(propertyY + k)) { + if (this.environment.containsProperty(propertyX + k) + || this.environment.containsProperty(propertyY + k)) { Map current = entry.getValue().getInputHeaderMappingExpression(); if (current.containsKey("0")) { ((Map) current.get("0")).put(k, headerMapping.get(k)); @@ -136,7 +133,8 @@ public void setConfiguration(Map config headerMapping = entry.getValue().getOutputHeaderMappingExpression(); if (!CollectionUtils.isEmpty(headerMapping)) { for (Object k : headerMapping.keySet()) { - if (this.environment.containsProperty(propertyX + k) || this.environment.containsProperty(propertyY + k)) { + if (this.environment.containsProperty(propertyX + k) + || this.environment.containsProperty(propertyY + k)) { Map current = entry.getValue().getOutputHeaderMappingExpression(); if (current.containsKey("0")) { ((Map) current.get("0")).put(k, headerMapping.get(k)); @@ -154,8 +152,7 @@ public void setConfiguration(Map config } @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @@ -220,8 +217,7 @@ public Map getOutputHeaderMappingExpression() { return outputHeaderMappingExpression; } - public void setOutputHeaderMappingExpression( - Map outputHeaderMappingExpression) { + public void setOutputHeaderMappingExpression(Map outputHeaderMappingExpression) { this.outputHeaderMappingExpression = outputHeaderMappingExpression; } @@ -234,4 +230,5 @@ public void setCopyInputHeaders(boolean copyInputHeaders) { } } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java index ffc2a4c8c..3977ec026 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java @@ -45,11 +45,9 @@ public class FunctionRegistration implements BeanNameAware { /** - * Suffix used to add to the name of FunctionRegistration bean that - * corresponds to the an actual function bean. It is often used when - * the actual function bean may not be a java Function (e.g., Kotlin) - * and certain custom wrapping is required. - *
+ * Suffix used to add to the name of FunctionRegistration bean that corresponds to the + * an actual function bean. It is often used when the actual function bean may not be + * a java Function (e.g., Kotlin) and certain custom wrapping is required.
* NOTE: This is not intended as oublis API */ public static String REGISTRATION_NAME_SUFFIX = "_registration"; @@ -63,11 +61,11 @@ public class FunctionRegistration implements BeanNameAware { private Type type; /** - * In certain cased, {@link FunctionRegistration} needs to know details - * about the user function. For example, when the user provides a BiConsumer, - * we wrap that in a regular Function. There are actions that a downstream client - * needs to take based on the type information of the wrapped function such as - * not creating an output binding when the wrapped type is a BiConsumer. + * In certain cased, {@link FunctionRegistration} needs to know details about the user + * function. For example, when the user provides a BiConsumer, we wrap that in a + * regular Function. There are actions that a downstream client needs to take based on + * the type information of the wrapped function such as not creating an output binding + * when the wrapped type is a BiConsumer. */ private Object userFunction; @@ -118,11 +116,13 @@ public FunctionRegistration properties(Map properties) { public FunctionRegistration type(Type type) { this.type = type; - if (KotlinDetector.isKotlinPresent() && this.target instanceof KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper) { + if (KotlinDetector.isKotlinPresent() + && this.target instanceof KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper) { return this; } - Type discoveredFunctionType = type; //FunctionTypeUtils.discoverFunctionTypeFromClass(this.target.getClass()); - if (discoveredFunctionType == null) { // only valid for Kafka Stream KStream[] return type. + Type discoveredFunctionType = type; // FunctionTypeUtils.discoverFunctionTypeFromClass(this.target.getClass()); + if (discoveredFunctionType == null) { // only valid for Kafka Stream KStream[] + // return type. return null; } @@ -130,20 +130,24 @@ public FunctionRegistration type(Type type) { Class outputType = FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(discoveredFunctionType)); if (inputType != null && inputType != Object.class && outputType != null && outputType != Object.class) { - Assert.isTrue((inputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) - && outputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type)))), + Assert.isTrue( + (inputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + && outputType + .isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type)))), "Discovered function type does not match provided function type. Discovered: " - + discoveredFunctionType + "; Provided: " + type); + + discoveredFunctionType + "; Provided: " + type); } else if (inputType == null && outputType != Object.class) { - Assert.isTrue(outputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))), + Assert.isTrue( + outputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))), "Discovered function type does not match provided function type. Discovered: " - + discoveredFunctionType + "; Provided: " + type); + + discoveredFunctionType + "; Provided: " + type); } else if (outputType == null && inputType != Object.class) { - Assert.isTrue(inputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))), + Assert.isTrue( + inputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))), "Discovered function type does not match provided function type. Discovered: " - + discoveredFunctionType + "; Provided: " + type); + + discoveredFunctionType + "; Provided: " + type); } return this; @@ -197,4 +201,5 @@ public Object getUserFunction() { public void setUserFunction(Object userFunction) { this.userFunction = userFunction; } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionTypeProcessor.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionTypeProcessor.java index ba716fd9b..5dac402a3 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionTypeProcessor.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionTypeProcessor.java @@ -83,12 +83,12 @@ private boolean isCoreJavaType(String className) { } private boolean isFunction(Class beanType) { - return Function.class.isAssignableFrom(beanType) - || Consumer.class.isAssignableFrom(beanType) - || Supplier.class.isAssignableFrom(beanType); + return Function.class.isAssignableFrom(beanType) || Consumer.class.isAssignableFrom(beanType) + || Supplier.class.isAssignableFrom(beanType); } - private static final class ReflectiveProcessorBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution { + private static final class ReflectiveProcessorBeanFactoryInitializationAotContribution + implements BeanFactoryInitializationAotContribution { private final Class[] typeHints; @@ -97,24 +97,25 @@ private ReflectiveProcessorBeanFactoryInitializationAotContribution(Class[] t } @Override - public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) { + public void applyTo(GenerationContext generationContext, + BeanFactoryInitializationCode beanFactoryInitializationCode) { RuntimeHints runtimeHints = generationContext.getRuntimeHints(); for (int i = 0; i < typeHints.length; i++) { - runtimeHints.reflection().registerType(typeHints[i], MemberCategory.PUBLIC_FIELDS, - MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + runtimeHints.reflection() + .registerType(typeHints[i], MemberCategory.PUBLIC_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS, + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); } // known static types - runtimeHints.reflection().registerType(MessageUtils.MessageStructureWithCaseInsensitiveHeaderKeys.class, - MemberCategory.INVOKE_PUBLIC_METHODS); - + runtimeHints.reflection() + .registerType(MessageUtils.MessageStructureWithCaseInsensitiveHeaderKeys.class, + MemberCategory.INVOKE_PUBLIC_METHODS); // temporary due to bug in boot - runtimeHints.reflection().registerType(Ssl.class, - MemberCategory.INVOKE_PUBLIC_METHODS); - runtimeHints.reflection().registerType(ServerNameSslBundle.class, - MemberCategory.INVOKE_PUBLIC_METHODS); + runtimeHints.reflection().registerType(Ssl.class, MemberCategory.INVOKE_PUBLIC_METHODS); + runtimeHints.reflection().registerType(ServerNameSslBundle.class, MemberCategory.INVOKE_PUBLIC_METHODS); } } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java index 5c8a2f391..7556430a7 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java @@ -44,8 +44,7 @@ * @author Semyon Fishman * @author Oleg Zhurakousky */ -public class FunctionalSpringApplication - extends org.springframework.boot.SpringApplication { +public class FunctionalSpringApplication extends org.springframework.boot.SpringApplication { /** * Flag to say that context is functional beans. @@ -65,8 +64,7 @@ public class FunctionalSpringApplication public FunctionalSpringApplication(Class... primarySources) { super(primarySources); setApplicationContextFactory(ApplicationContextFactory.ofContextClass(GenericApplicationContext.class)); - if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", - null)) { + if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", null)) { setWebApplicationType(WebApplicationType.REACTIVE); } else { @@ -78,13 +76,11 @@ public static void main(String[] args) throws Exception { FunctionalSpringApplication.run(new Class[0], args); } - public static ConfigurableApplicationContext run(Class primarySource, - String... args) { + public static ConfigurableApplicationContext run(Class primarySource, String... args) { return run(new Class[] { primarySource }, args); } - public static ConfigurableApplicationContext run(Class[] primarySources, - String[] args) { + public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) { return new FunctionalSpringApplication(primarySources).run(args); } @@ -117,13 +113,11 @@ else if (source instanceof Class) { handler = BeanUtils.instantiateClass(type); } - ApplicationContextInitializer initializer = - (ApplicationContextInitializer) handler; + ApplicationContextInitializer initializer = (ApplicationContextInitializer) handler; initializer.initialize(context); functional = true; } - else if (Function.class.isAssignableFrom(type) - || Consumer.class.isAssignableFrom(type) + else if (Function.class.isAssignableFrom(type) || Consumer.class.isAssignableFrom(type) || Supplier.class.isAssignableFrom(type)) { Class functionType = type; Object function = handler; @@ -132,7 +126,7 @@ else if (Function.class.isAssignableFrom(type) BeanDefinitionRegistry bdRegistry = (BeanDefinitionRegistry) beanFactory; if (!ObjectUtils.isEmpty(context.getBeanNamesForType(functionType))) { stream(context.getBeanNamesForType(functionType)) - .forEach(beanName -> bdRegistry.registerAlias(beanName, "function")); + .forEach(beanName -> bdRegistry.registerAlias(beanName, "function")); } else { this.register((GenericApplicationContext) context, function, functionType); @@ -152,13 +146,10 @@ else if (Function.class.isAssignableFrom(type) private void register(GenericApplicationContext context, Object function, Class functionType) { context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>( - handler(context, function, functionType)) - .type(functionType)); + () -> new FunctionRegistration<>(handler(context, function, functionType)).type(functionType)); } - private Object handler(GenericApplicationContext generic, Object handler, - Class type) { + private Object handler(GenericApplicationContext generic, Object handler, Class type) { if (handler == null) { handler = generic.getAutowireCapableBeanFactory().createBean(type); } @@ -167,8 +158,7 @@ private Object handler(GenericApplicationContext generic, Object handler, @Override protected void load(ApplicationContext context, Object[] sources) { - if (!context.getEnvironment().getProperty(SPRING_FUNCTIONAL_ENABLED, - Boolean.class, false)) { + if (!context.getEnvironment().getProperty(SPRING_FUNCTIONAL_ENABLED, Boolean.class, false)) { super.load(context, sources); } } @@ -176,17 +166,14 @@ protected void load(ApplicationContext context, Object[] sources) { private void defaultProperties(ConfigurableApplicationContext context) { MutablePropertySources sources = context.getEnvironment().getPropertySources(); if (!sources.contains(DEFAULT_PROPERTIES)) { - sources.addLast( - new MapPropertySource(DEFAULT_PROPERTIES, Collections.emptyMap())); + sources.addLast(new MapPropertySource(DEFAULT_PROPERTIES, Collections.emptyMap())); } @SuppressWarnings("unchecked") - Map source = (Map) sources.get(DEFAULT_PROPERTIES) - .getSource(); + Map source = (Map) sources.get(DEFAULT_PROPERTIES).getSource(); Map map = new HashMap<>(source); map.put(SPRING_FUNCTIONAL_ENABLED, "true"); map.put(SPRING_WEB_APPLICATION_TYPE, getWebApplicationType()); - sources.replace(DEFAULT_PROPERTIES, - new MapPropertySource(DEFAULT_PROPERTIES, map)); + sources.replace(DEFAULT_PROPERTIES, new MapPropertySource(DEFAULT_PROPERTIES, map)); } } 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 17edef463..fc8104b9d 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 @@ -20,12 +20,12 @@ import org.springframework.messaging.Message; /** - * Java-based strategy to assist with determining the name of the route-to function definition. - * Once an implementation is registered as a bean in application context - * it will be picked up by the {@link RoutingFunction}. + * Java-based strategy to assist with determining the name of the route-to function + * definition. 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. + * 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 @@ -34,18 +34,19 @@ public interface MessageRoutingCallback { /** - * Computes and returns an instance of {@link String}, which encapsulates, - * at the very minimum, a 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 - * message for downstream use didn't exist, resulting in repeated transformation, type conversion etc. - * + * 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 message for downstream use didn't exist, resulting in + * repeated transformation, type conversion etc. * @param message input message * @return instance of {@link String} containing the result of the routing computation */ default String routingResult(Message message) { return (String) message.getHeaders().get(FunctionProperties.FUNCTION_DEFINITION); } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PollableBean.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PollableBean.java index 807a873f4..858ab03aa 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PollableBean.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PollableBean.java @@ -27,25 +27,24 @@ /** * - * A marker and qualifier annotation to signal that - * annotated functional factory method is a bean (e.g., Supplier, Function or Consumer) - * that also needs to be polled periodically. + * A marker and qualifier annotation to signal that annotated functional factory method is + * a bean (e.g., Supplier, Function or Consumer) that also needs to be polled + * periodically.
+ * This has special significance to the reactive suppliers (e.g., + * {@code Supplier>}), since by default they are treated as producers of an + * infinite stream. However if such suppliers produce a finite stream they may need to be + * triggered again.
*
- * This has special significance to the reactive suppliers (e.g., {@code Supplier>}), - * since by default they are treated as producers of an infinite stream. - * However if such suppliers produce a finite stream they may need to be triggered again. - *
- *
- * NOTE: The spring-cloud-function framework provides no default post processing behavior for this annotation. This - * means that annotating a factory method with this annotation will not have any effect without some application/framework - * specific post processing (see spring-cloud-stream as an example). - * + * NOTE: The spring-cloud-function framework provides no default post processing behavior + * for this annotation. This means that annotating a factory method with this annotation + * will not have any effect without some application/framework specific post processing + * (see spring-cloud-stream as an example). * * @author Oleg Zhurakousky * @since 3.0 * */ -@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Bean @Documented @@ -53,11 +52,11 @@ /** * Signals to the post processors of this annotation that the result produced by the - * annotated {@link Supplier} has to be split. Specifics on how to split and what - * to split are left to the underlying framework. - * - * @return true if the resulting stream produced by the - * annotated {@link Supplier} has to be split. + * annotated {@link Supplier} has to be split. Specifics on how to split and what to + * split are left to the underlying framework. + * @return true if the resulting stream produced by the annotated {@link Supplier} has + * to be split. */ boolean splittable() default true; + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PostProcessingFunction.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PostProcessingFunction.java index d9c17ae45..3289b8d22 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PostProcessingFunction.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PostProcessingFunction.java @@ -21,16 +21,15 @@ import org.springframework.messaging.Message; /** - * Strategy for implementing function with post processing behavior. - *
- * The core framework only provides support for the post-processing behavior. - * The actual invocation of post-processing is left to the end user or the framework which - * integrates Spring Cloud Function. This is because post-processing can mean different things - * in different execution contexts. See {@link #postProcess(Message)} method for more information. + * Strategy for implementing function with post processing behavior.
+ * The core framework only provides support for the post-processing behavior. The actual + * invocation of post-processing is left to the end user or the framework which integrates + * Spring Cloud Function. This is because post-processing can mean different things in + * different execution contexts. See {@link #postProcess(Message)} method for more + * information. * * @param - input type * @param - output type - * * @author Oleg Zhurakousky * @since 4.0.3 * @@ -44,19 +43,22 @@ default O apply(I t) { } /** - * Will post process the result of this's function invocation after this function has been triggered. - *
- * This operation is not managed/invoked by the core functionality of the Spring Cloud Function. - * It is specifically designed as a hook for other frameworks and extensions to invoke after - * this function was "triggered" and there is a requirement to do some post processing. The word "triggered" - * can mean different things in different execution contexts. For example, in spring-cloud-stream it means - * that the function has been invoked and the result of the function has been sent to the target destination. - * - * The boolean value argument - 'success' - allows the triggering framework to signal success or - * failure of its triggering operation whatever that may mean. + * Will post process the result of this's function invocation after this function has + * been triggered.
+ * This operation is not managed/invoked by the core functionality of the Spring Cloud + * Function. It is specifically designed as a hook for other frameworks and extensions + * to invoke after this function was "triggered" and there is a requirement to do some + * post processing. The word "triggered" can mean different things in different + * execution contexts. For example, in spring-cloud-stream it means that the function + * has been invoked and the result of the function has been sent to the target + * destination. * - * @param result - the result of function invocation as an instance of {@link Message} including all the metadata as message headers. + * The boolean value argument - 'success' - allows the triggering framework to signal + * success or failure of its triggering operation whatever that may mean. + * @param result - the result of function invocation as an instance of {@link Message} + * including all the metadata as message headers. */ default void postProcess(Message result) { } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java index 732a08e70..20381e0b4 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java @@ -58,7 +58,8 @@ import org.springframework.util.StringUtils; /** - * Implementation of {@link FunctionRegistry} capable of discovering functions in {@link BeanFactory}. + * Implementation of {@link FunctionRegistry} capable of discovering functions in + * {@link BeanFactory}. * * @author Oleg Zhurakousky * @author Soby Chacko @@ -67,8 +68,10 @@ public class BeanFactoryAwareFunctionRegistry extends SimpleFunctionRegistry imp private GenericApplicationContext applicationContext; - public BeanFactoryAwareFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, - JsonMapper jsonMapper, @Nullable FunctionProperties functionProperties, @Nullable FunctionInvocationHelper> functionInvocationHelper) { + public BeanFactoryAwareFunctionRegistry(ConversionService conversionService, + CompositeMessageConverter messageConverter, JsonMapper jsonMapper, + @Nullable FunctionProperties functionProperties, + @Nullable FunctionInvocationHelper> functionInvocationHelper) { super(conversionService, messageConverter, jsonMapper, functionProperties, functionInvocationHelper); } @@ -83,10 +86,9 @@ public void setApplicationContext(ApplicationContext applicationContext) throws */ @Override public int size() { - return this.applicationContext.getBeanNamesForType(Supplier.class).length + - this.applicationContext.getBeanNamesForType(Function.class).length + - this.applicationContext.getBeanNamesForType(Consumer.class).length + - super.size(); + return this.applicationContext.getBeanNamesForType(Supplier.class).length + + this.applicationContext.getBeanNamesForType(Function.class).length + + this.applicationContext.getBeanNamesForType(Consumer.class).length + super.size(); } /* @@ -96,16 +98,11 @@ public int size() { public Set getNames(Class type) { Set registeredNames = super.getNames(type); if (type == null) { - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(Function.class))); - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(Supplier.class))); - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(Consumer.class))); - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(BiFunction.class))); - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(BiConsumer.class))); + registeredNames.addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(Function.class))); + registeredNames.addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(Supplier.class))); + registeredNames.addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(Consumer.class))); + registeredNames.addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(BiFunction.class))); + registeredNames.addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(BiConsumer.class))); registeredNames .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(FunctionRegistration.class))); } @@ -118,23 +115,25 @@ public Set getNames(Class type) { @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public T lookup(Class type, String functionDefinition, String... expectedOutputMimeTypes) { - functionDefinition = StringUtils.hasText(functionDefinition) - ? functionDefinition - : this.applicationContext.getEnvironment().getProperty(FunctionProperties.FUNCTION_DEFINITION, ""); - if (!this.applicationContext.containsBean(functionDefinition) || !KotlinUtils.isKotlinType(this.applicationContext.getBean(functionDefinition))) { + functionDefinition = StringUtils.hasText(functionDefinition) ? functionDefinition + : this.applicationContext.getEnvironment().getProperty(FunctionProperties.FUNCTION_DEFINITION, ""); + if (!this.applicationContext.containsBean(functionDefinition) + || !KotlinUtils.isKotlinType(this.applicationContext.getBean(functionDefinition))) { functionDefinition = this.normalizeFunctionDefinition(functionDefinition); } if (!isFunctionDefinitionEligible(functionDefinition)) { return null; } if (!StringUtils.hasText(functionDefinition)) { - Collection functionalBeans = this.getNames(null).stream() - .filter(name -> !RoutingFunction.FUNCTION_NAME.equals(name)) - .filter(name -> !RoutingFunction.DEFAULT_ROUTE_HANDLER.equals(name)) - .collect(Collectors.toList()); + Collection functionalBeans = this.getNames(null) + .stream() + .filter(name -> !RoutingFunction.FUNCTION_NAME.equals(name)) + .filter(name -> !RoutingFunction.DEFAULT_ROUTE_HANDLER.equals(name)) + .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(functionalBeans) && functionalBeans.size() > 1) { - logger.warn("Multiple functional beans were found " + functionalBeans + ", thus can't determine default function definition. Please " - + "use 'spring.cloud.function.definition' property to explicitly define it. "); + logger.warn("Multiple functional beans were found " + functionalBeans + + ", thus can't determine default function definition. Please " + + "use 'spring.cloud.function.definition' property to explicitly define it. "); } } @@ -143,7 +142,8 @@ public T lookup(Class type, String functionDefinition, String... expected synchronized (syncInstance) { if (function == null) { Set functionRegistratioinNames = super.getNames(null); - String[] functionNames = StringUtils.delimitedListToStringArray(functionDefinition.replaceAll(",", "|").trim(), "|"); + String[] functionNames = StringUtils + .delimitedListToStringArray(functionDefinition.replaceAll(",", "|").trim(), "|"); for (String functionName : functionNames) { if (functionRegistratioinNames.contains(functionName)) { if (logger.isDebugEnabled()) { @@ -158,45 +158,55 @@ public T lookup(Class type, String functionDefinition, String... expected if (functionCandidate instanceof FunctionRegistration) { functionRegistration = (FunctionRegistration) functionCandidate; } - else if (functionCandidate instanceof BiFunction || functionCandidate instanceof BiConsumer) { - functionRegistration = this.registerMessagingBiFunction(functionCandidate, functionName); + else if (functionCandidate instanceof BiFunction + || functionCandidate instanceof BiConsumer) { + functionRegistration = this.registerMessagingBiFunction(functionCandidate, + functionName); } else if (KotlinUtils.isKotlinType(functionCandidate)) { - KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper wrapper = - new KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper(functionCandidate); + KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper wrapper = new KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper( + functionCandidate); wrapper.setName(functionName); wrapper.setBeanFactory(this.applicationContext.getBeanFactory()); functionRegistration = wrapper.getFunctionRegistration(); } else if (this.isFunctionPojo(functionCandidate, functionName)) { - Method functionalMethod = FunctionTypeUtils.discoverFunctionalMethod(functionCandidate.getClass()); + Method functionalMethod = FunctionTypeUtils + .discoverFunctionalMethod(functionCandidate.getClass()); functionCandidate = this.proxyTarget(functionCandidate, functionalMethod); functionType = FunctionTypeUtils.fromFunctionMethod(functionalMethod); - // GH-1307: Mark this as a POJO function for special handling + // GH-1307: Mark this as a POJO function for special + // handling functionRegistration = new FunctionRegistration(functionCandidate, functionName) - .type(functionType) - .properties(Collections.singletonMap("isPojoFunction", "true")); + .type(functionType) + .properties(Collections.singletonMap("isPojoFunction", "true")); } else if (this.isSpecialFunctionRegistration(functionNames, functionName)) { - functionRegistration = this.applicationContext - .getBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, FunctionRegistration.class); + functionRegistration = this.applicationContext.getBean( + functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, + FunctionRegistration.class); } else { - functionType = FunctionTypeUtils.discoverFunctionType(functionCandidate, functionName, this.applicationContext); + functionType = FunctionTypeUtils.discoverFunctionType(functionCandidate, functionName, + this.applicationContext); } if (logger.isDebugEnabled()) { - logger.debug("Discovered function type for: " + functionDefinition + " - " + functionType); + logger.debug( + "Discovered function type for: " + functionDefinition + " - " + functionType); } if (functionRegistration == null) { - functionRegistration = new FunctionRegistration(functionCandidate, functionName).type(functionType); + functionRegistration = new FunctionRegistration(functionCandidate, functionName) + .type(functionType); } - // Certain Kafka Streams functions such as KStream[] return types could be null (esp when using Kotlin). + // Certain Kafka Streams functions such as KStream[] return + // types could be null (esp when using Kotlin). this.register(functionRegistration); } else { if (logger.isDebugEnabled()) { - logger.debug("Function '" + functionName + "' is not available in FunctionCatalog or BeanFactory"); + logger.debug("Function '" + functionName + + "' is not available in FunctionCatalog or BeanFactory"); } } } @@ -224,8 +234,11 @@ private FunctionRegistration registerMessagingBiFunction(Object userFunction, St + "Other signatures are not supported at the moment."); } - ResolvableType messageType = ResolvableType.forClassWithGenerics(Message.class, ResolvableType.forType(inputType1)); - Type biFunctionWrapperType = ResolvableType.forClassWithGenerics(Function.class, messageType, ResolvableType.forType(inputType2)).getType(); + ResolvableType messageType = ResolvableType.forClassWithGenerics(Message.class, + ResolvableType.forType(inputType1)); + Type biFunctionWrapperType = ResolvableType + .forClassWithGenerics(Function.class, messageType, ResolvableType.forType(inputType2)) + .getType(); Function wrapperFunction = message -> { Object payload = ((Message) message).getPayload(); @@ -241,7 +254,8 @@ private FunctionRegistration registerMessagingBiFunction(Object userFunction, St } }; - FunctionRegistration functionRegistration = new FunctionRegistration<>(wrapperFunction, functionName).type(biFunctionWrapperType); + FunctionRegistration functionRegistration = new FunctionRegistration<>(wrapperFunction, functionName) + .type(biFunctionWrapperType); functionRegistration.setUserFunction(userFunction); return functionRegistration; } @@ -253,7 +267,8 @@ private Object discoverFunctionInBeanFactory(String functionName) { } else { try { - functionCandidate = BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.applicationContext.getBeanFactory(), Object.class, functionName); + functionCandidate = BeanFactoryAnnotationUtils + .qualifiedBeanOfType(this.applicationContext.getBeanFactory(), Object.class, functionName); } catch (Exception e) { // ignore since there is no safe isAvailable-kind of method @@ -268,21 +283,19 @@ protected boolean containsFunction(String functionName) { } private boolean isFunctionPojo(Object functionCandidate, String functionName) { - return !functionCandidate.getClass().isSynthetic() - && !(functionCandidate instanceof Supplier) - && !(functionCandidate instanceof Function) - && !(functionCandidate instanceof Consumer) - && !this.applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX); + return !functionCandidate.getClass().isSynthetic() && !(functionCandidate instanceof Supplier) + && !(functionCandidate instanceof Function) && !(functionCandidate instanceof Consumer) + && !this.applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX); } /** - * At the moment 'special function registration' simply implies that a bean under the provided functionName - * may have already been wrapped and registered as FunuctionRegistration with BeanFactory under the name of - * the function suffixed with {@link FunctionRegistration#REGISTRATION_NAME_SUFFIX} - * (e.g., 'myKotlinFunction_registration'). - *

+ * At the moment 'special function registration' simply implies that a bean under the + * provided functionName may have already been wrapped and registered as + * FunuctionRegistration with BeanFactory under the name of the function suffixed with + * {@link FunctionRegistration#REGISTRATION_NAME_SUFFIX} (e.g., + * 'myKotlinFunction_registration').
+ *
* At the moment only Kotlin module does this - * * @param functionCandidate candidate for FunctionInvocationWrapper instance * @param functionName the name of the function * @return true if this function candidate qualifies @@ -303,4 +316,5 @@ public Object invoke(MethodInvocation invocation) throws Throwable { }); return pf.getProxy(); } + } 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 b19172c87..9d98921b5 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 @@ -23,12 +23,12 @@ import org.springframework.util.StringUtils; /** - * Wrapper that acts as around advise over function invocation. - * If registered as bean it will be autowired into {@link FunctionInvocationWrapper}. - * Keep in mind that it only affects imperative invocations where input is {@link Message} + * Wrapper that acts as around advise over function invocation. If registered as bean it + * will be autowired into {@link FunctionInvocationWrapper}. Keep in mind that it only + * affects imperative invocations where input is {@link Message} * - * NOTE: This API is experimental and and could change without notice. It is - * intended for internal use only (e.g., spring-cloud-sleuth) + * NOTE: This API is experimental and and could change without notice. It is intended for + * internal use only (e.g., spring-cloud-sleuth) * * @author Oleg Zhurakousky * @since 3.1 @@ -40,7 +40,8 @@ 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())) { + if (functionalTracingEnabled && !(input instanceof Publisher) && input instanceof Message + && !FunctionTypeUtils.isCollectionOfMessage(targetFunction.getOutputType())) { Object result = this.doApply(input, targetFunction); targetFunction.wrapped = false; return result; @@ -51,4 +52,5 @@ public final Object apply(Object input, FunctionInvocationWrapper targetFunction } protected abstract Object doApply(Object input, FunctionInvocationWrapper targetFunction); + } 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 b3314ac3f..b3cff0355 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 @@ -72,14 +72,12 @@ import org.springframework.util.ReflectionUtils; 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 { @@ -93,25 +91,23 @@ private FunctionTypeUtils() { } public static Type functionType(Type input, Type output) { - return ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forType(input), ResolvableType.forType(output)).getType(); + return ResolvableType + .forClassWithGenerics(Function.class, ResolvableType.forType(input), ResolvableType.forType(output)) + .getType(); } public static Type consumerType(Type input) { - return ResolvableType.forClassWithGenerics(Consumer.class, - ResolvableType.forType(input)).getType(); + return ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forType(input)).getType(); } public static Type supplierType(Type output) { - return ResolvableType.forClassWithGenerics(Supplier.class, - ResolvableType.forType(output)).getType(); + return ResolvableType.forClassWithGenerics(Supplier.class, ResolvableType.forType(output)).getType(); } /** - * Will return 'true' if the provided type is a {@link Collection} type. - * This also includes collections wrapped in {@link Message}. For example, - * If provided type is {@code Message>} this operation will return 'true'. - * + * Will return 'true' if the provided type is a {@link Collection} type. This also + * includes collections wrapped in {@link Message}. For example, If provided type is + * {@code Message>} this operation will return 'true'. * @param type type to interrogate * @return 'true' if this type represents a {@link Collection}. Otherwise 'false'. */ @@ -147,9 +143,8 @@ public static boolean isJsonNode(Type type) { } /** - * A convenience method identical to {@link #getImmediateGenericType(Type, int)} - * for cases when provided 'type' is {@link Publisher} or {@link Message}. - * + * A convenience method identical to {@link #getImmediateGenericType(Type, int)} for + * cases when provided 'type' is {@link Publisher} or {@link Message}. * @param type type to interrogate * @return generic type if possible otherwise the same type as provided */ @@ -165,43 +160,54 @@ public static Type getGenericType(Type type) { } /** - * Effectively converts {@link Type} which could be {@link ParameterizedType} to raw Class (no generics). + * Effectively converts {@link Type} which could be {@link ParameterizedType} to raw + * Class (no generics). * @param type actual {@link Type} instance - * @return instance of {@link Class} as raw representation of the provided {@link Type} + * @return instance of {@link Class} as raw representation of the provided + * {@link Type} */ public static Class getRawType(Type type) { if (type instanceof WildcardType) { Type[] upperbounds = ((WildcardType) type).getUpperBounds(); /* - * Kotlin may have something like this which is technically a whildcard yet it has upper/lower types. - * See GH-1260 + * Kotlin may have something like this which is + * technically a whildcard yet it has upper/lower types. See GH-1260 */ return ObjectUtils.isEmpty(upperbounds) ? Object.class : getRawType(upperbounds[0]); } - return ResolvableType.forType(type).getRawClass() == null ? Object.class : ResolvableType.forType(type).getRawClass(); + return ResolvableType.forType(type).getRawClass() == null ? Object.class + : ResolvableType.forType(type).getRawClass(); } /** - * Will attempt to discover functional methods on the class. It's applicable for POJOs as well as - * functional classes in `java.util.function` package. For the later the names of the methods are - * well known (`apply`, `accept` and `get`). For the former it will attempt to discover a single method - * following semantics described in (see {@link FunctionalInterface}) - * + * Will attempt to discover functional methods on the class. It's applicable for POJOs + * as well as functional classes in `java.util.function` package. For the later the + * names of the methods are well known (`apply`, `accept` and `get`). For the former + * it will attempt to discover a single method following semantics described in (see + * {@link FunctionalInterface}) * @param pojoFunctionClass the class to introspect * @return functional method */ public static Method discoverFunctionalMethod(Class pojoFunctionClass) { if (Supplier.class.isAssignableFrom(pojoFunctionClass)) { - return Stream.of(ReflectionUtils.getAllDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic() - && m.getName().equals("get")).findFirst().get(); - } - else if (Consumer.class.isAssignableFrom(pojoFunctionClass) || BiConsumer.class.isAssignableFrom(pojoFunctionClass)) { - return Stream.of(ReflectionUtils.getAllDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic() - && m.getName().equals("accept")).findFirst().get(); - } - else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class.isAssignableFrom(pojoFunctionClass)) { - return Stream.of(ReflectionUtils.getAllDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic() - && m.getName().equals("apply")).findFirst().get(); + return Stream.of(ReflectionUtils.getAllDeclaredMethods(pojoFunctionClass)) + .filter(m -> !m.isSynthetic() && m.getName().equals("get")) + .findFirst() + .get(); + } + else if (Consumer.class.isAssignableFrom(pojoFunctionClass) + || BiConsumer.class.isAssignableFrom(pojoFunctionClass)) { + return Stream.of(ReflectionUtils.getAllDeclaredMethods(pojoFunctionClass)) + .filter(m -> !m.isSynthetic() && m.getName().equals("accept")) + .findFirst() + .get(); + } + else if (Function.class.isAssignableFrom(pojoFunctionClass) + || BiFunction.class.isAssignableFrom(pojoFunctionClass)) { + return Stream.of(ReflectionUtils.getAllDeclaredMethods(pojoFunctionClass)) + .filter(m -> !m.isSynthetic() && m.getName().equals("apply")) + .findFirst() + .get(); } List methods = new ArrayList<>(); @@ -210,16 +216,13 @@ else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class. methods.add(method); } - }, method -> - !method.getDeclaringClass().isAssignableFrom(Object.class) - && !method.isSynthetic() && !method.isBridge() && !method.isVarArgs()); + }, method -> !method.getDeclaringClass().isAssignableFrom(Object.class) && !method.isSynthetic() + && !method.isBridge() && !method.isVarArgs()); if (methods.size() > 1) { for (Method candidadteMethod : methods) { - if (candidadteMethod.getName().equals("apply") - || candidadteMethod.getName().equals("accept") - || candidadteMethod.getName().equals("get") - || candidadteMethod.getName().equals("invoke")) { + if (candidadteMethod.getName().equals("apply") || candidadteMethod.getName().equals("accept") + || candidadteMethod.getName().equals("get") || candidadteMethod.getName().equals("invoke")) { return candidadteMethod; } } @@ -258,10 +261,10 @@ else if (Function0.class.isAssignableFrom(functionalClass)) { } /** - * Discovers the function {@link Type} based on the signature of a factory method. - * For example, given the following method {@code Function, Message> uppercase()} of - * class Foo - {@code Type type = discoverFunctionTypeFromFunctionFactoryMethod(Foo.class, "uppercase");} - * + * Discovers the function {@link Type} based on the signature of a factory method. For + * example, given the following method + * {@code Function, Message> uppercase()} of class Foo - + * {@code Type type = discoverFunctionTypeFromFunctionFactoryMethod(Foo.class, "uppercase");} * @param clazz instance of Class containing the factory method * @param methodName factory method name * @return type of the function @@ -271,10 +274,10 @@ public static Type discoverFunctionTypeFromFunctionFactoryMethod(Class clazz, } /** - * Discovers the function {@link Type} based on the signature of a factory method. - * For example, given the following method {@code Function, Message> uppercase()} of - * class Foo - {@code Type type = discoverFunctionTypeFromFunctionFactoryMethod(Foo.class, "uppercase");} - * + * Discovers the function {@link Type} based on the signature of a factory method. For + * example, given the following method + * {@code Function, Message> uppercase()} of class Foo - + * {@code Type type = discoverFunctionTypeFromFunctionFactoryMethod(Foo.class, "uppercase");} * @param method factory method * @return type of the function */ @@ -283,8 +286,9 @@ public static Type discoverFunctionTypeFromFunctionFactoryMethod(Method method) } /** - * Unlike {@link #discoverFunctionTypeFromFunctionFactoryMethod(Class, String)}, this method discovers function - * type from the well known method of Function(apply), Supplier(get) or Consumer(accept). + * Unlike {@link #discoverFunctionTypeFromFunctionFactoryMethod(Class, String)}, this + * method discovers function type from the well known method of Function(apply), + * Supplier(get) or Consumer(accept). * @param functionMethod functional method * @return type of the function */ @@ -293,11 +297,10 @@ public static Type discoverFunctionTypeFromFunctionMethod(Method functionMethod) return null; } Assert.isTrue( - functionMethod.getName().equals("apply") || - functionMethod.getName().equals("accept") || - functionMethod.getName().equals("get") || - functionMethod.getName().equals("invoke"), - "Only Supplier, Function or Consumer supported at the moment. Was " + functionMethod.getDeclaringClass()); + functionMethod.getName().equals("apply") || functionMethod.getName().equals("accept") + || functionMethod.getName().equals("get") || functionMethod.getName().equals("invoke"), + "Only Supplier, Function or Consumer supported at the moment. Was " + + functionMethod.getDeclaringClass()); ResolvableType functionType; if (functionMethod.getName().equals("apply") || functionMethod.getName().equals("invoke")) { @@ -351,7 +354,8 @@ public static int getOutputCount(FunctionInvocationWrapper function) { } /** - * In the event the input type is {@link ParameterizedType} this method returns its generic type. + * In the event the input type is {@link ParameterizedType} this method returns its + * generic type. * @param functionType instance of function type * @return generic type or input type */ @@ -361,7 +365,8 @@ public static Type getComponentTypeOfInputType(Type functionType) { } /** - * In the event the output type is {@link ParameterizedType} this method returns its generic type. + * In the event the output type is {@link ParameterizedType} this method returns its + * generic type. * @param functionType instance of function type * @return generic type or output type */ @@ -371,7 +376,8 @@ public static Type getComponentTypeOfOutputType(Type functionType) { } /** - * Will resolve @{@link ResolvableType} to {@link Type} preserving all the resolved generics. + * Will resolve @{@link ResolvableType} to {@link Type} preserving all the resolved + * generics. * @param typeWithGenerics - instance of {@link ResolvableType}. * @return - {@link Type} representation of the provided {@link ResolvableType}. */ @@ -383,8 +389,9 @@ public static Type resolveType(ResolvableType typeWithGenerics) { ResolvableType genericType = typeWithGenerics.getGenerics()[i]; resolvedGenerics.add(ResolvableType.forType(resolveType(genericType))); } - return ResolvableType.forClassWithGenerics(typeWithGenerics.getRawClass(), - resolvedGenerics.toArray(new ResolvableType[0])).getType(); + return ResolvableType + .forClassWithGenerics(typeWithGenerics.getRawClass(), resolvedGenerics.toArray(new ResolvableType[0])) + .getType(); } else { return typeWithGenerics.resolve(); @@ -403,11 +410,10 @@ public static Type getOutputType(Type functionType) { } else { ResolvableType resolvableFunctionType = isSupplier(functionType) - ? ResolvableType.forType(functionType).as(Supplier.class) - : ResolvableType.forType(functionType).as(Function.class); - ResolvableType generics = isSupplier(functionType) - ? resolvableFunctionType.getGenerics()[0] - : resolvableFunctionType.getGenerics()[1]; + ? ResolvableType.forType(functionType).as(Supplier.class) + : ResolvableType.forType(functionType).as(Function.class); + ResolvableType generics = isSupplier(functionType) ? resolvableFunctionType.getGenerics()[0] + : resolvableFunctionType.getGenerics()[1]; Type outputType = FunctionTypeUtils.resolveType(generics); return outputType == null || outputType instanceof TypeVariable ? Object.class : outputType; } @@ -415,7 +421,7 @@ public static Type getOutputType(Type functionType) { /** * Returns input type of function type that represents Function or Consumer. - * @param functionType the Type of Function or Consumer + * @param functionType the Type of Function or Consumer * @return the input type as {@link Type} */ public static Type getInputType(Type functionType) { @@ -430,8 +436,8 @@ public static Type getInputType(Type functionType) { } else { ResolvableType resolvableFunctionType = isConsumer(functionType) - ? ResolvableType.forType(functionType).as(Consumer.class) - : ResolvableType.forType(functionType).as(Function.class); + ? ResolvableType.forType(functionType).as(Consumer.class) + : ResolvableType.forType(functionType).as(Function.class); ResolvableType generics = resolvableFunctionType.getGenerics()[0]; Type inputType = FunctionTypeUtils.resolveType(generics); return inputType == null || inputType instanceof TypeVariable ? Object.class : inputType; @@ -439,16 +445,19 @@ public static Type getInputType(Type functionType) { } @SuppressWarnings("rawtypes") - public static Type discoverFunctionType(Object function, String functionName, GenericApplicationContext applicationContext) { + public static Type discoverFunctionType(Object function, String functionName, + GenericApplicationContext applicationContext) { if (function instanceof RoutingFunction) { return ROUTING_FUNCTION_TYPE; } else if (function instanceof FunctionRegistration) { return ((FunctionRegistration) function).getType(); } - if (applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX)) { // for Kotlin primarily + 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(); } @@ -457,10 +466,10 @@ else if (function instanceof FunctionRegistration) { if (type == null || type instanceof Class) { boolean beanDefinitionExists = false; String functionBeanDefinitionName = discoverDefinitionName(functionName, applicationContext); - beanDefinitionExists = applicationContext.getBeanFactory().containsBeanDefinition(functionBeanDefinitionName); + beanDefinitionExists = applicationContext.getBeanFactory() + .containsBeanDefinition(functionBeanDefinitionName); if (applicationContext.containsBean("&" + functionName)) { - Class objectType = applicationContext.getBean("&" + functionName, FactoryBean.class) - .getObjectType(); + Class objectType = applicationContext.getBean("&" + functionName, FactoryBean.class).getObjectType(); return FunctionTypeUtils.discoverFunctionTypeFromClass(objectType); } @@ -468,11 +477,13 @@ else if (function instanceof FunctionRegistration) { if (beanDefinitionExists) { Type t = FunctionTypeUtils.getImmediateGenericType(type, 0); if (t == null || t == Object.class) { - type = FunctionContextUtils.findType(applicationContext.getBeanFactory(), functionBeanDefinitionName); + type = FunctionContextUtils.findType(applicationContext.getBeanFactory(), + functionBeanDefinitionName); } } else if (!(type instanceof ParameterizedType)) { - String beanDefinitionName = discoverBeanDefinitionNameByQualifier(applicationContext.getBeanFactory(), functionName); + String beanDefinitionName = discoverBeanDefinitionNameByQualifier(applicationContext.getBeanFactory(), + functionName); if (StringUtils.hasText(beanDefinitionName)) { type = FunctionContextUtils.findType(applicationContext.getBeanFactory(), beanDefinitionName); } @@ -497,6 +508,7 @@ public static String discoverBeanDefinitionNameByQualifier(ListableBeanFactory b } return null; } + public static Type getImmediateGenericType(Type type, int index) { if (type instanceof ParameterizedType) { return ((ParameterizedType) type).getActualTypeArguments()[index]; @@ -550,7 +562,8 @@ public static boolean isMessage(Type type) { */ public static boolean isOutputArray(Type functionType) { Type outputType = FunctionTypeUtils.getOutputType(functionType); - return outputType instanceof GenericArrayType || outputType instanceof Class && ((Class) outputType).isArray(); + return outputType instanceof GenericArrayType + || outputType instanceof Class && ((Class) outputType).isArray(); } public static boolean isSupplier(Type type) { @@ -586,23 +599,26 @@ static Type fromFunctionMethod(Method functionalMethod) { Type functionType = null; switch (parameterTypes.length) { - case 0: - 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(); - } - else { - functionType = ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forMethodParameter(functionalMethod, 0), - ResolvableType.forMethodReturnType(functionalMethod)).getType(); - } - break; - default: - throw new UnsupportedOperationException("Functional method: " + functionalMethod + " is not supported"); + case 0: + 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(); + } + else { + functionType = ResolvableType + .forClassWithGenerics(Function.class, ResolvableType.forMethodParameter(functionalMethod, 0), + ResolvableType.forMethodReturnType(functionalMethod)) + .getType(); + } + break; + default: + throw new UnsupportedOperationException("Functional method: " + functionalMethod + " is not supported"); } return functionType; } @@ -624,14 +640,16 @@ else if (type instanceof ParameterizedType) { 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); + Assert.isTrue(type instanceof Class, + "Must be one of Supplier, Function, Consumer" + " or FunctionRegistration. Was " + type); } Class candidateType = (Class) type; - Assert.isTrue(Supplier.class.isAssignableFrom(candidateType) - || (KotlinDetector.isKotlinPresent() && (Function0.class.isAssignableFrom(candidateType) || Function1.class.isAssignableFrom(candidateType))) + Assert.isTrue( + Supplier.class.isAssignableFrom(candidateType) + || (KotlinDetector.isKotlinPresent() && (Function0.class.isAssignableFrom(candidateType) + || Function1.class.isAssignableFrom(candidateType))) || Function.class.isAssignableFrom(candidateType) || Consumer.class.isAssignableFrom(candidateType) || FunctionRegistration.class.isAssignableFrom(candidateType) @@ -647,13 +665,14 @@ private static void assertSupportedTypes(Type type) { || DoubleSupplier.class.isAssignableFrom(candidateType) || DoubleFunction.class.isAssignableFrom(candidateType) || ToDoubleFunction.class.isAssignableFrom(candidateType) - || type.getTypeName().startsWith("org.springframework.context.annotation.ConfigurationClassEnhancer"), - "Must be one of Supplier, Function, Consumer" - + " or FunctionRegistration. Was " + type); + || 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) { - if (type instanceof ParameterizedType && FunctionRegistration.class.isAssignableFrom(((Class) ((ParameterizedType) type).getRawType()))) { + if (type instanceof ParameterizedType + && FunctionRegistration.class.isAssignableFrom(((Class) ((ParameterizedType) type).getRawType()))) { type = getImmediateGenericType(type, 0); if (type instanceof ParameterizedType) { type = getImmediateGenericType(type, 0); @@ -662,7 +681,8 @@ private static Type extractReactiveType(Type type) { return type; } - private static String discoverDefinitionName(String functionDefinition, GenericApplicationContext applicationContext) { + private static String discoverDefinitionName(String functionDefinition, + GenericApplicationContext applicationContext) { String[] aliases = applicationContext.getAliases(functionDefinition); for (String alias : aliases) { if (applicationContext.getBeanFactory().containsBeanDefinition(alias)) { @@ -671,4 +691,5 @@ private static String discoverDefinitionName(String functionDefinition, GenericA } return functionDefinition; } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/HeaderEnricher.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/HeaderEnricher.java index 23fc39646..ffda68a99 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/HeaderEnricher.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/HeaderEnricher.java @@ -34,11 +34,10 @@ import org.springframework.util.Assert; /** - * Class responsible for processing `input-header-mapping-expression` - * and modifying message headers accordingly. + * Class responsible for processing `input-header-mapping-expression` and modifying + * message headers accordingly. * * @author Oleg Zhurakousky - * * @since 3.1.3 * */ @@ -74,7 +73,8 @@ public Object apply(Object input) { messageBuilder.setHeader(keyValueExpressionEntry.getKey(), value); } catch (Exception e) { - String message = "Failed while evaluating expression \"" + keyValueExpressionEntry.getValue() + "\" on incoming message"; + String message = "Failed while evaluating expression \"" + keyValueExpressionEntry.getValue() + + "\" on incoming message"; if (logger.isDebugEnabled()) { logger.warn(message + ": " + input, e); } 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 2761018f0..1e91a7532 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 @@ -79,14 +79,10 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; - - - /** - * Implementation of {@link FunctionCatalog} and {@link FunctionRegistry} which - * does not depend on Spring's {@link BeanFactory}. - * Each function must be registered with it explicitly to benefit from features - * such as type conversion, composition, POJO etc. + * Implementation of {@link FunctionCatalog} and {@link FunctionRegistry} which does not + * depend on Spring's {@link BeanFactory}. Each function must be registered with it + * explicitly to benefit from features such as type conversion, composition, POJO etc. * * @author Oleg Zhurakousky * @author Roman Samarev @@ -94,9 +90,12 @@ * @author Chris Bono */ public class SimpleFunctionRegistry implements FunctionRegistry { + protected Log logger = LogFactory.getLog(this.getClass()); + /* - * - do we care about FunctionRegistration after it's been registered? What additional value does it bring? + * - do we care about FunctionRegistration after it's been registered? What additional + * value does it bring? * */ @@ -117,8 +116,8 @@ public class SimpleFunctionRegistry implements FunctionRegistry { @Autowired(required = false) private FunctionAroundWrapper functionAroundWrapper; - public SimpleFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, JsonMapper jsonMapper, - @Nullable FunctionProperties functionProperties, + public SimpleFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, + JsonMapper jsonMapper, @Nullable FunctionProperties functionProperties, @Nullable FunctionInvocationHelper> functionInvocationHelper) { Assert.notNull(messageConverter, "'messageConverter' must not be null"); Assert.notNull(jsonMapper, "'jsonMapper' must not be null"); @@ -130,8 +129,8 @@ public SimpleFunctionRegistry(ConversionService conversionService, CompositeMess } /** - * Will add provided {@link MessageConverter}s to the head of the stack of the existing MessageConverters. - * + * Will add provided {@link MessageConverter}s to the head of the stack of the + * existing MessageConverters. * @param messageConverters list of {@link MessageConverter}s. */ public void addMessageConverters(Collection messageConverters) { @@ -140,7 +139,8 @@ public void addMessageConverters(Collection messageConverters) } } - public SimpleFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, JsonMapper jsonMapper) { + public SimpleFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, + JsonMapper jsonMapper) { this(conversionService, messageConverter, jsonMapper, null, null); } @@ -179,7 +179,8 @@ private boolean isRegistrationEligible(FunctionRegistration registration) { if (registration.getTarget().getClass().getName().equals(definition)) { return false; } - else if (registration.getNames().contains(definition) || registration.getTarget().getClass().getName().contains(definition)) { + else if (registration.getNames().contains(definition) + || registration.getTarget().getClass().getName().contains(definition)) { return false; } } @@ -197,7 +198,7 @@ boolean isFunctionDefinitionEligible(String functionDefinition) { return true; } - //----- + // ----- @Override public Set getNames(Class type) { @@ -239,27 +240,23 @@ else if (logger.isDebugEnabled()) { } /** - * This method will make sure that if there is only one function in catalog - * it can be looked up by any name or no name. - * It does so by attempting to determine the default function name - * (the only function in catalog) and checking if it matches the provided name - * replacing it if it does not. + * This method will make sure that if there is only one function in catalog it can be + * looked up by any name or no name. It does so by attempting to determine the default + * function name (the only function in catalog) and checking if it matches the + * provided name replacing it if it does not. */ String normalizeFunctionDefinition(String functionDefinition) { - functionDefinition = StringUtils.hasText(functionDefinition) - ? functionDefinition.replaceAll(",", "|") + functionDefinition = StringUtils.hasText(functionDefinition) ? functionDefinition.replaceAll(",", "|") : System.getProperty(FunctionProperties.FUNCTION_DEFINITION, ""); Set names = this.getNames(null); if (!names.contains(functionDefinition)) { List eligibleFunction = names.stream() - .filter(name -> !RoutingFunction.FUNCTION_NAME.equals(name)) - .filter(name -> !RoutingFunction.DEFAULT_ROUTE_HANDLER.equals(name)) - .collect(Collectors.toList()); - if (eligibleFunction.size() == 1 - && !eligibleFunction.get(0).equals(functionDefinition) - && !functionDefinition.contains("|") - && !eligibleFunction.get(0).startsWith("&")) { + .filter(name -> !RoutingFunction.FUNCTION_NAME.equals(name)) + .filter(name -> !RoutingFunction.DEFAULT_ROUTE_HANDLER.equals(name)) + .collect(Collectors.toList()); + if (eligibleFunction.size() == 1 && !eligibleFunction.get(0).equals(functionDefinition) + && !functionDefinition.contains("|") && !eligibleFunction.get(0).startsWith("&")) { functionDefinition = eligibleFunction.get(0); } } @@ -271,12 +268,11 @@ String normalizeFunctionDefinition(String functionDefinition) { */ private FunctionInvocationWrapper findFunctionInFunctionRegistrations(String functionName) { FunctionRegistration functionRegistration = this.functionRegistrations.stream() - .filter(fr -> fr.getNames().contains(functionName)) - .findFirst() - .orElseGet(() -> null); - FunctionInvocationWrapper function = functionRegistration != null - ? this.invocationWrapperInstance(functionName, functionRegistration.getTarget(), functionRegistration.getType()) - : null; + .filter(fr -> fr.getNames().contains(functionName)) + .findFirst() + .orElseGet(() -> null); + FunctionInvocationWrapper function = functionRegistration != null ? this.invocationWrapperInstance(functionName, + functionRegistration.getTarget(), functionRegistration.getType()) : null; if (functionRegistration != null) { Object userFunction = functionRegistration.getUserFunction(); if (userFunction instanceof BiConsumer && function != null) { @@ -292,8 +288,7 @@ private FunctionInvocationWrapper findFunctionInFunctionRegistrations(String fun } } // GH-1307: Mark POJO functions for special Message wrapping behavior - if (functionRegistration != null && - functionRegistration.getProperties().containsKey("isPojoFunction")) { + if (functionRegistration != null && functionRegistration.getProperties().containsKey("isPojoFunction")) { try { String isPojoValue = functionRegistration.getProperties().get("isPojoFunction"); function.setPojoFunction(Boolean.parseBoolean(isPojoValue)); @@ -309,7 +304,8 @@ private FunctionInvocationWrapper findFunctionInFunctionRegistrations(String fun * */ private FunctionInvocationWrapper compose(Class type, String functionDefinition) { - String[] functionNames = StringUtils.delimitedListToStringArray(functionDefinition.replaceAll(",", "|").trim(), "|"); + String[] functionNames = StringUtils.delimitedListToStringArray(functionDefinition.replaceAll(",", "|").trim(), + "|"); FunctionInvocationWrapper composedFunction = null; for (String functionName : functionNames) { @@ -317,7 +313,8 @@ private FunctionInvocationWrapper compose(Class type, String functionDefiniti if (function == null) { if (logger.isDebugEnabled()) { logger.debug("Failed to locate function '" + functionName + "' for function definition '" - + functionDefinition + " amongst existing function registrations. Will check with the bean factory"); + + functionDefinition + + " amongst existing function registrations. Will check with the bean factory"); } return null; } @@ -326,9 +323,10 @@ private FunctionInvocationWrapper compose(Class type, String functionDefiniti composedFunction = function; } else { - FunctionInvocationWrapper andThenFunction = - invocationWrapperInstance(functionName, function.getTarget(), function.inputType, function.outputType); - composedFunction = (FunctionInvocationWrapper) composedFunction.andThen((Function) andThenFunction); + FunctionInvocationWrapper andThenFunction = invocationWrapperInstance(functionName, + function.getTarget(), function.inputType, function.outputType); + composedFunction = (FunctionInvocationWrapper) composedFunction + .andThen((Function) andThenFunction); } composedFunction = this.enrichInputIfNecessary(composedFunction); composedFunction = this.enrichOutputIfNecessary(composedFunction); @@ -348,18 +346,21 @@ private FunctionInvocationWrapper enrichInputIfNecessary(FunctionInvocationWrapp return composedFunction; } String functionDefinition = composedFunction.getFunctionDefinition(); - Map configurationProperties = this.functionProperties.getConfiguration(); + Map configurationProperties = this.functionProperties + .getConfiguration(); if (!CollectionUtils.isEmpty(configurationProperties)) { - FunctionConfigurationProperties configuration = configurationProperties - .get(functionDefinition.replace("|", "").replace(",", "")); + FunctionConfigurationProperties configuration = configurationProperties + .get(functionDefinition.replace("|", "").replace(",", "")); if (configuration != null) { if (!CollectionUtils.isEmpty(configuration.getInputHeaderMappingExpression())) { BeanFactoryResolver beanResolver = this.functionProperties.getApplicationContext() != null - ? new BeanFactoryResolver(this.functionProperties.getApplicationContext()) - : null; - HeaderEnricher enricher = new HeaderEnricher(configuration.getInputHeaderMappingExpression(), beanResolver); - FunctionInvocationWrapper w = new FunctionInvocationWrapper("inputHeaderEnricher", enricher, Message.class, Message.class); - composedFunction = (FunctionInvocationWrapper) w.andThen((Function) composedFunction); + ? new BeanFactoryResolver(this.functionProperties.getApplicationContext()) : null; + HeaderEnricher enricher = new HeaderEnricher(configuration.getInputHeaderMappingExpression(), + beanResolver); + FunctionInvocationWrapper w = new FunctionInvocationWrapper("inputHeaderEnricher", enricher, + Message.class, Message.class); + composedFunction = (FunctionInvocationWrapper) w + .andThen((Function) composedFunction); composedFunction.functionDefinition = functionDefinition; } } @@ -372,19 +373,22 @@ private FunctionInvocationWrapper enrichOutputIfNecessary(FunctionInvocationWrap return composedFunction; } String functionDefinition = composedFunction.getFunctionDefinition(); - Map configurationProperties = this.functionProperties.getConfiguration(); + Map configurationProperties = this.functionProperties + .getConfiguration(); if (!CollectionUtils.isEmpty(configurationProperties)) { - FunctionConfigurationProperties configuration = configurationProperties - .get(functionDefinition.replace("|", "").replace(",", "")); + FunctionConfigurationProperties configuration = configurationProperties + .get(functionDefinition.replace("|", "").replace(",", "")); if (configuration != null) { if (!CollectionUtils.isEmpty(configuration.getOutputHeaderMappingExpression())) { BeanFactoryResolver beanResolver = this.functionProperties.getApplicationContext() != null - ? new BeanFactoryResolver(this.functionProperties.getApplicationContext()) - : null; - HeaderEnricher enricher = new HeaderEnricher(configuration.getOutputHeaderMappingExpression(), beanResolver); + ? new BeanFactoryResolver(this.functionProperties.getApplicationContext()) : null; + HeaderEnricher enricher = new HeaderEnricher(configuration.getOutputHeaderMappingExpression(), + beanResolver); Type mesageType = ResolvableType.forClassWithGenerics(Message.class, Object.class).getType(); - FunctionInvocationWrapper enricherWrapper = new FunctionInvocationWrapper("outputHeaderEnricher", enricher, mesageType, mesageType); - composedFunction = (FunctionInvocationWrapper) composedFunction.andThen((Function) enricherWrapper); + FunctionInvocationWrapper enricherWrapper = new FunctionInvocationWrapper("outputHeaderEnricher", + enricher, mesageType, mesageType); + composedFunction = (FunctionInvocationWrapper) composedFunction + .andThen((Function) enricherWrapper); composedFunction.functionDefinition = functionDefinition; } } @@ -395,14 +399,16 @@ private FunctionInvocationWrapper enrichOutputIfNecessary(FunctionInvocationWrap /* * */ - private FunctionInvocationWrapper invocationWrapperInstance(String functionDefinition, Object target, Type inputType, Type outputType) { + private FunctionInvocationWrapper invocationWrapperInstance(String functionDefinition, Object target, + Type inputType, Type outputType) { return new FunctionInvocationWrapper(functionDefinition, target, inputType, outputType); } /* * */ - private FunctionInvocationWrapper invocationWrapperInstance(String functionDefinition, Object target, Type functionType) { + private FunctionInvocationWrapper invocationWrapperInstance(String functionDefinition, Object target, + Type functionType) { return invocationWrapperInstance(functionDefinition, target, FunctionTypeUtils.isSupplier(functionType) ? null : FunctionTypeUtils.getInputType(functionType), FunctionTypeUtils.getOutputType(functionType)); @@ -412,7 +418,8 @@ private FunctionInvocationWrapper invocationWrapperInstance(String functionDefin * */ @SuppressWarnings("rawtypes") - public class FunctionInvocationWrapper implements Function, Consumer, Supplier, Runnable { + public class FunctionInvocationWrapper + implements Function, Consumer, Supplier, Runnable { private Object target; @@ -445,10 +452,10 @@ public class FunctionInvocationWrapper implements Function, Cons private Consumer skipInputConversionCallback; /* - * This is primarily to support Stream's ability to access - * un-converted payload (e.g., to evaluate expression on some attribute of a payload) - * It is not intended to remain here and will be removed as soon as particular elements - * of stream will be refactored to address this. + * This is primarily to support Stream's ability to access un-converted payload + * (e.g., to evaluate expression on some attribute of a payload) It is not + * intended to remain here and will be removed as soon as particular elements of + * stream will be refactored to address this. */ private Function enhancer; @@ -456,13 +463,13 @@ public class FunctionInvocationWrapper implements Function, Cons private boolean isPojoFunction; - FunctionInvocationWrapper(String functionDefinition, Object target, Type inputType, Type outputType) { + FunctionInvocationWrapper(String functionDefinition, Object target, Type inputType, Type outputType) { if (target instanceof PostProcessingFunction) { this.postProcessor = (PostProcessingFunction) target; } if (ClassUtils.isPresent("kotlin.jvm.functions.Function0", ClassUtils.getDefaultClassLoader()) - && target instanceof KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper kotlinFunction - && kotlinFunction.getKotlinLambdaTarget() instanceof PostProcessingFunction) { + && target instanceof KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper kotlinFunction + && kotlinFunction.getKotlinLambdaTarget() instanceof PostProcessingFunction) { this.postProcessor = (PostProcessingFunction) kotlinFunction.getKotlinLambdaTarget(); } this.target = target; @@ -493,8 +500,8 @@ public void postProcess() { this.postProcessor.postProcess(result); } catch (Exception ex) { - logger.warn("Failed to post process function " - + this.functionDefinition + "; Result of the invocation before post processing is " + result, ex); + logger.warn("Failed to post process function " + this.functionDefinition + + "; Result of the invocation before post processing is " + result, ex); } finally { this.unconvertedResult.remove(); @@ -533,7 +540,8 @@ public boolean isPrototype() { public void setSkipInputConversion(boolean skipInputConversion) { if (logger.isDebugEnabled() && skipInputConversion) { - logger.debug("'skipInputConversion' was explicitely set to true. No input conversion will be attempted"); + logger + .debug("'skipInputConversion' was explicitely set to true. No input conversion will be attempted"); } this.skipInputConversion = skipInputConversion; if (this.skipInputConversionCallback != null) { @@ -547,17 +555,17 @@ void setSkipInputConversionCallback(Consumer skipInputConversionCallbac public void setSkipOutputConversion(boolean skipOutputConversion) { if (logger.isDebugEnabled() && skipOutputConversion) { - logger.debug("'skipOutputConversion' was explicitely set to true. No output conversion will be attempted"); + logger.debug( + "'skipOutputConversion' was explicitely set to true. No output conversion will be attempted"); } this.skipOutputConversion = skipOutputConversion; } /** - * !!! INTERNAL USE ONLY !!! - * This is primarily to support s-c-Stream's ability to access - * un-converted payload (e.g., to evaluate expression on some attribute of a payload) - * It is not intended to remain here and will be removed as soon as particular elements - * of stream will be refactored to address this. + * !!! INTERNAL USE ONLY !!! This is primarily to support s-c-Stream's ability to + * access un-converted payload (e.g., to evaluate expression on some attribute of + * a payload) It is not intended to remain here and will be removed as soon as + * particular elements of stream will be refactored to address this. */ public Function getEnhancer() { return this.enhancer; @@ -568,11 +576,10 @@ public Type getOutputType() { } /** - * !!! INTERNAL USE ONLY !!! - * This is primarily to support s-c-Stream's ability to access - * un-converted payload (e.g., to evaluate expression on some attribute of a payload) - * It is not intended to remain here and will be removed as soon as particular elements - * of stream will be refactored to address this. + * !!! INTERNAL USE ONLY !!! This is primarily to support s-c-Stream's ability to + * access un-converted payload (e.g., to evaluate expression on some attribute of + * a payload) It is not intended to remain here and will be removed as soon as + * particular elements of stream will be refactored to address this. */ public void setEnhancer(Function enhancer) { this.enhancer = enhancer; @@ -587,15 +594,19 @@ public Type getInputType() { } /** - * Return the actual {@link Type} of the item of the provided type. - * This method is context specific and is not a general purpose utility method. The context is that the provided - * {@link Type} may represent the input/output of a function where such type could be wrapped in - * {@link Message}, {@link Flux} or {@link Mono}, so this method returns generic value of such type or itself if not wrapped. - * @param type typically input or output Type of the function (see {@link #getInputType()} or {@link #getOutputType()}. + * Return the actual {@link Type} of the item of the provided type. This method is + * context specific and is not a general purpose utility method. The context is + * that the provided {@link Type} may represent the input/output of a function + * where such type could be wrapped in {@link Message}, {@link Flux} or + * {@link Mono}, so this method returns generic value of such type or itself if + * not wrapped. + * @param type typically input or output Type of the function (see + * {@link #getInputType()} or {@link #getOutputType()}. * @return the type of the item if wrapped otherwise the provided type. */ public Type getItemType(Type type) { - if (FunctionTypeUtils.isPublisher(type) || FunctionTypeUtils.isMessage(type) || FunctionTypeUtils.isTypeCollection(type)) { + if (FunctionTypeUtils.isPublisher(type) || FunctionTypeUtils.isMessage(type) + || FunctionTypeUtils.isTypeCollection(type)) { type = FunctionTypeUtils.getGenericType(type); } return type; @@ -614,7 +625,7 @@ public Class getRawInputType() { */ @Override public Object apply(Object input) { - if (logger.isDebugEnabled() && !(input instanceof Publisher)) { + if (logger.isDebugEnabled() && !(input instanceof Publisher)) { logger.debug("Invoking function " + this); } @@ -674,7 +685,6 @@ public boolean isOutputTypeMessage() { return FunctionTypeUtils.isMessage(this.outputType); } - public boolean isRoutingFunction() { return this.target instanceof RoutingFunction; } @@ -685,19 +695,23 @@ public boolean isRoutingFunction() { @SuppressWarnings("unchecked") @Override public Function andThen(Function after) { - Assert.isTrue(after instanceof FunctionInvocationWrapper, "Composed function must be an instanceof FunctionInvocationWrapper."); + Assert.isTrue(after instanceof FunctionInvocationWrapper, + "Composed function must be an instanceof FunctionInvocationWrapper."); if (FunctionTypeUtils.isMultipleArgumentType(this.inputType) || FunctionTypeUtils.isMultipleArgumentType(this.outputType) || FunctionTypeUtils.isMultipleArgumentType(((FunctionInvocationWrapper) after).inputType) || FunctionTypeUtils.isMultipleArgumentType(((FunctionInvocationWrapper) after).outputType)) { - throw new UnsupportedOperationException("Composition of functions with multiple arguments is not supported at the moment"); + throw new UnsupportedOperationException( + "Composition of functions with multiple arguments is not supported at the moment"); } FunctionInvocationWrapper afterWrapper = (FunctionInvocationWrapper) after; - //see GH-1141 for this code snippet - if ((this.getTarget() instanceof Supplier || this.getTarget() instanceof Function) && FunctionTypeUtils.isPublisher(this.getOutputType()) - && afterWrapper.getTarget() instanceof Consumer && !FunctionTypeUtils.isPublisher(afterWrapper.getInputType())) { + // see GH-1141 for this code snippet + if ((this.getTarget() instanceof Supplier || this.getTarget() instanceof Function) + && FunctionTypeUtils.isPublisher(this.getOutputType()) + && afterWrapper.getTarget() instanceof Consumer + && !FunctionTypeUtils.isPublisher(afterWrapper.getInputType())) { Consumer wrapper = new ConsumerWrapper((Consumer) afterWrapper.getTarget()); afterWrapper.target = wrapper; afterWrapper.inputType = this.outputType; @@ -710,35 +724,42 @@ public Function andThen(Function aft Type composedFunctionType; if (afterWrapper.outputType == null) { - composedFunctionType = (this.inputType == null) ? - ResolvableType.forClassWithGenerics(Supplier.class, ResolvableType.forType(Object.class)).getType() : - ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forType(this.inputType)).getType(); + composedFunctionType = (this.inputType == null) + ? ResolvableType.forClassWithGenerics(Supplier.class, ResolvableType.forType(Object.class)) + .getType() + : ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forType(this.inputType)) + .getType(); } else if (this.inputType == null && afterWrapper.outputType != null) { ResolvableType composedOutputType; if (FunctionTypeUtils.isFlux(this.outputType)) { - composedOutputType = ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(afterWrapper.outputType)); + composedOutputType = ResolvableType.forClassWithGenerics(Flux.class, + ResolvableType.forType(afterWrapper.outputType)); } else if (FunctionTypeUtils.isMono(this.outputType)) { - composedOutputType = ResolvableType.forClassWithGenerics(Mono.class, ResolvableType.forType(afterWrapper.outputType)); + composedOutputType = ResolvableType.forClassWithGenerics(Mono.class, + ResolvableType.forType(afterWrapper.outputType)); } else { composedOutputType = ResolvableType.forType(afterWrapper.outputType); } - composedFunctionType = ResolvableType.forClassWithGenerics(Supplier.class, composedOutputType).getType(); + composedFunctionType = ResolvableType.forClassWithGenerics(Supplier.class, composedOutputType) + .getType(); } else if (this.outputType == null) { throw new IllegalArgumentException("Can NOT compose anything with Consumer"); } else { - composedFunctionType = ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forType(this.inputType), - ResolvableType.forType(((FunctionInvocationWrapper) after).outputType)).getType(); + composedFunctionType = ResolvableType + .forClassWithGenerics(Function.class, ResolvableType.forType(this.inputType), + ResolvableType.forType(((FunctionInvocationWrapper) after).outputType)) + .getType(); } String composedName = this.functionDefinition + "|" + afterWrapper.functionDefinition; - FunctionInvocationWrapper composedFunction = invocationWrapperInstance(composedName, rawComposedFunction, composedFunctionType); + FunctionInvocationWrapper composedFunction = invocationWrapperInstance(composedName, rawComposedFunction, + composedFunctionType); composedFunction.setSkipInputConversionCallback((skipInputConversion) -> { this.setSkipInputConversion(skipInputConversion); afterWrapper.setSkipInputConversion(skipInputConversion); @@ -764,12 +785,14 @@ public String getFunctionDefinition() { */ @Override public String toString() { - return this.functionDefinition + (this.isComposed() ? "" : "<" + this.inputType + ", " + this.outputType + ">"); + return this.functionDefinition + + (this.isComposed() ? "" : "<" + this.inputType + ", " + this.outputType + ">"); } /** * Returns true if this function wrapper represents a composed function. - * @return true if this function wrapper represents a composed function otherwise false + * @return true if this function wrapper represents a composed function otherwise + * false */ public boolean isComposed() { return this.composed; @@ -820,7 +843,8 @@ private boolean isTypePublisher(Type type) { } /** - * Will return Object.class if type is represented as TypeVariable(T) or WildcardType(?). + * Will return Object.class if type is represented as TypeVariable(T) or + * WildcardType(?). */ private Type normalizeType(Type type) { if (type != null) { @@ -833,13 +857,13 @@ private Type normalizeType(Type type) { * */ private Class getRawClassFor(@Nullable Type type) { - return type instanceof TypeVariable || type instanceof WildcardType - ? Object.class + return type instanceof TypeVariable || type instanceof WildcardType ? Object.class : FunctionTypeUtils.getRawType(type); } /** - * Will wrap the result in a Message if necessary and will copy input headers to the output message. + * Will wrap the result in a Message if necessary and will copy input headers to + * the output message. */ private Object enrichInvocationResultIfNecessary(Object input, Object result) { if (result != null && !(result instanceof Publisher) && input instanceof Message) { @@ -847,7 +871,9 @@ private Object enrichInvocationResultIfNecessary(Object input, Object result) { result = functionInvocationHelper.postProcessResult(result, (Message) input); } if (!(result instanceof Message) && !FunctionTypeUtils.isCollectionOfMessage(this.outputType)) { - result = MessageBuilder.withPayload(result).copyHeaders(this.sanitizeHeaders(((Message) input).getHeaders())).build(); + result = MessageBuilder.withPayload(result) + .copyHeaders(this.sanitizeHeaders(((Message) input).getHeaders())) + .build(); } } return result; @@ -868,7 +894,8 @@ private Map sanitizeHeaders(MessageHeaders headers) { @SuppressWarnings("unchecked") private Object fluxifyInputIfNecessary(Object input) { - if (input instanceof Message && !((Message) input).getHeaders().containsKey("user-agent") && this.isConsumer() && !this.isInputTypePublisher()) { + if (input instanceof Message && !((Message) input).getHeaders().containsKey("user-agent") + && this.isConsumer() && !this.isInputTypePublisher()) { return input; } if (FunctionTypeUtils.isMultipleArgumentType(this.inputType)) { @@ -891,21 +918,24 @@ private Object fluxifyInputIfNecessary(Object input) { if ((!treatPayloadAsPlainText && JsonMapper.isJsonStringRepresentsCollection(payload)) && !FunctionTypeUtils.isTypeCollection(this.inputType) && !FunctionTypeUtils.isTypeArray(this.inputType)) { - logger.debug("Actual input represents a collection while input type of the function does not represent a collection. " + - "Therefore framework will attempt invoke function for each element in the collection."); - MessageHeaders headers = input instanceof Message ? ((Message) input).getHeaders() : new MessageHeaders(Collections.emptyMap()); + logger + .debug("Actual input represents a collection while input type of the function does not represent a collection. " + + "Therefore framework will attempt invoke function for each element in the collection."); + MessageHeaders headers = input instanceof Message ? ((Message) input).getHeaders() + : new MessageHeaders(Collections.emptyMap()); Collection collectionPayload = jsonMapper.fromJson(payload, Collection.class); Class inputClass = FunctionTypeUtils.getRawType(this.inputType); if (this.isInputTypeMessage()) { - inputClass = FunctionTypeUtils.getRawType(FunctionTypeUtils.getImmediateGenericType(this.inputType, 0)); + inputClass = FunctionTypeUtils + .getRawType(FunctionTypeUtils.getImmediateGenericType(this.inputType, 0)); } if (!inputClass.isAssignableFrom(Object.class) && !inputClass.isAssignableFrom(byte[].class)) { logger.debug("Converting JSON string representing collection to a list of Messages. Function '" + this + "' will be invoked iteratively"); input = collectionPayload.stream() - .map(p -> MessageBuilder.withPayload(p).copyHeaders(headers).build()) - .collect(Collectors.toList()); + .map(p -> MessageBuilder.withPayload(p).copyHeaders(headers).build()) + .collect(Collectors.toList()); } } } @@ -915,22 +945,25 @@ private Object fluxifyInputIfNecessary(Object input) { input = FunctionTypeUtils.isMono(this.inputType) ? Mono.empty() : Flux.empty(); } else if (input instanceof Message && ((Message) input).getPayload() instanceof Iterable) { - input = FunctionTypeUtils.isMono(this.inputType) ? Mono.just(input) : Flux.just(input).flatMap(v -> { - if (logger.isDebugEnabled()) { - logger.debug("Creating Flux from Iterable: " + ((Message) v).getPayload()); - } - return Flux.fromIterable((Iterable) ((Message) v).getPayload()); - }); + input = FunctionTypeUtils.isMono(this.inputType) ? Mono.just(input) + : Flux.just(input).flatMap(v -> { + if (logger.isDebugEnabled()) { + logger.debug("Creating Flux from Iterable: " + ((Message) v).getPayload()); + } + return Flux.fromIterable((Iterable) ((Message) v).getPayload()); + }); } else if (input instanceof Iterable) { - input = FunctionTypeUtils.isMono(this.inputType) ? Mono.just(input) : Flux.fromIterable((Iterable) input); + input = FunctionTypeUtils.isMono(this.inputType) ? Mono.just(input) + : Flux.fromIterable((Iterable) input); } else { input = FunctionTypeUtils.isMono(this.inputType) ? Mono.just(input) : Flux.just(input); } } - else if (!(input instanceof Publisher) && input instanceof Iterable && !FunctionTypeUtils.isTypeCollection(this.inputType)) { + else if (!(input instanceof Publisher) && input instanceof Iterable + && !FunctionTypeUtils.isTypeCollection(this.inputType)) { input = Flux.fromIterable((Iterable) input); } return input; @@ -952,20 +985,24 @@ private Object invokeFunction(Object convertedInput) { Object result; if (!this.isTypePublisher(this.inputType) && convertedInput instanceof Publisher publisherInput) { result = publisherInput instanceof Mono - ? Mono.from(publisherInput).map(value -> this.invokeFunctionAndEnrichResultIfNecessary(value)) - .doOnError(ex -> logger.error("Failed to invoke function '" + this.functionDefinition + "'", (Throwable) ex)) - : Flux.from(publisherInput).map(value -> this.invokeFunctionAndEnrichResultIfNecessary(value)) - .doOnError(ex -> logger.error("Failed to invoke function '" + this.functionDefinition + "'", (Throwable) ex)); + ? Mono.from(publisherInput) + .map(value -> this.invokeFunctionAndEnrichResultIfNecessary(value)) + .doOnError(ex -> logger.error("Failed to invoke function '" + this.functionDefinition + "'", + (Throwable) ex)) + : Flux.from(publisherInput) + .map(value -> this.invokeFunctionAndEnrichResultIfNecessary(value)) + .doOnError(ex -> logger.error("Failed to invoke function '" + this.functionDefinition + "'", + (Throwable) ex)); } else { result = this.invokeFunctionAndEnrichResultIfNecessary(convertedInput); if (result instanceof Flux flux) { - result = flux.doOnError(ex -> logger.error("Failed to invoke function '" - + this.functionDefinition + "'", (Throwable) ex)); + result = flux.doOnError(ex -> logger + .error("Failed to invoke function '" + this.functionDefinition + "'", (Throwable) ex)); } else if (result instanceof Mono mono) { - result = mono.doOnError(ex -> logger.error("Failed to invoke function '" - + this.functionDefinition + "'", (Throwable) ex)); + result = mono.doOnError(ex -> logger + .error("Failed to invoke function '" + this.functionDefinition + "'", (Throwable) ex)); } } return result; @@ -999,7 +1036,8 @@ else if (value instanceof Mono) { inputValue = this.extractValueFromOriginalValueHolderIfNecessary(value); } - if (!(this.target instanceof PassThruFunction) && inputValue instanceof Message && !this.isInputTypeMessage()) { + if (!(this.target instanceof PassThruFunction) && inputValue instanceof Message + && !this.isInputTypeMessage()) { inputValue = ((Message) inputValue).getPayload(); } @@ -1008,7 +1046,8 @@ else if (value instanceof Mono) { } Object result; - if (inputValue != null && inputValue.getClass().getName().equals("org.springframework.kafka.support.KafkaNull")) { + if (inputValue != null + && inputValue.getClass().getName().equals("org.springframework.kafka.support.KafkaNull")) { result = ((Function) this.target).apply(null); } else { @@ -1028,26 +1067,24 @@ else if (value instanceof Mono) { private Publisher postProcessFunction(Publisher result, AtomicReference> firstInputMessage) { if (FunctionTypeUtils.isPublisher(this.inputType) && FunctionTypeUtils.isPublisher(this.outputType)) { if (!FunctionTypeUtils.getRawType(FunctionTypeUtils.getImmediateGenericType(this.inputType, 0)) - .isAssignableFrom(Void.class) - && !FunctionTypeUtils.getRawType(FunctionTypeUtils.getImmediateGenericType(this.outputType, 0)) - .isAssignableFrom(Void.class)) { + .isAssignableFrom(Void.class) + && !FunctionTypeUtils.getRawType(FunctionTypeUtils.getImmediateGenericType(this.outputType, 0)) + .isAssignableFrom(Void.class)) { if (result instanceof Mono) { return Mono.from((result)).map(v -> { - if (firstInputMessage.get() != null && CloudEventMessageUtils - .isCloudEvent(firstInputMessage.get())) { - return functionInvocationHelper.postProcessResult(v, - firstInputMessage.get()); + if (firstInputMessage.get() != null + && CloudEventMessageUtils.isCloudEvent(firstInputMessage.get())) { + return functionInvocationHelper.postProcessResult(v, firstInputMessage.get()); } return v; }); } else { return Flux.from((result)).map(v -> { - if (firstInputMessage.get() != null && CloudEventMessageUtils - .isCloudEvent(firstInputMessage.get())) { - return functionInvocationHelper.postProcessResult(v, - firstInputMessage.get()); + if (firstInputMessage.get() != null + && CloudEventMessageUtils.isCloudEvent(firstInputMessage.get())) { + return functionInvocationHelper.postProcessResult(v, firstInputMessage.get()); } return v; }); @@ -1066,30 +1103,32 @@ private Object invokeConsumer(Object convertedInput) { Object result = null; if (this.isTypePublisher(this.inputType)) { if (convertedInput instanceof Flux fluxInput) { - result = fluxInput - .transform(flux -> { - flux = Flux.from((Publisher) flux).map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)); - ((Consumer) this.target).accept(flux); - return Mono.ignoreElements((Flux) flux); - }).then(); + result = fluxInput.transform(flux -> { + flux = Flux.from((Publisher) flux) + .map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)); + ((Consumer) this.target).accept(flux); + return Mono.ignoreElements((Flux) flux); + }).then(); } else { - result = ((Mono) convertedInput) - .transform(mono -> { - mono = Mono.from((Publisher) mono).map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)); - ((Consumer) this.target).accept(mono); - return Mono.ignoreElements((Mono) mono); - }).then(); + result = ((Mono) convertedInput).transform(mono -> { + mono = Mono.from((Publisher) mono) + .map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)); + ((Consumer) this.target).accept(mono); + return Mono.ignoreElements((Mono) mono); + }).then(); } } else if (convertedInput instanceof Publisher publisherInput) { result = convertedInput instanceof Mono ? Mono.from(publisherInput) - .map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)) - .doOnNext((Consumer) this.target).then() + .map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)) + .doOnNext((Consumer) this.target) + .then() : Flux.from(publisherInput) - .map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)) - .doOnNext((Consumer) this.target).then(); + .map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)) + .doOnNext((Consumer) this.target) + .then(); } else { Object extractedValue = this.extractValueFromOriginalValueHolderIfNecessary(convertedInput); @@ -1118,12 +1157,14 @@ private Object[] parseMultipleValueArguments(Object multipleValueArgument, int a } return parsedArgumentValues; } - throw new UnsupportedOperationException("At the moment only Tuple-based function are supporting multiple arguments"); + throw new UnsupportedOperationException( + "At the moment only Tuple-based function are supporting multiple arguments"); } @SuppressWarnings("unchecked") private boolean isInputConversionNecessary(Object input, Type type) { - if (type == null || this.getRawClassFor(type) == Void.class || this.target instanceof RoutingFunction || this.isComposed() || this.target instanceof PassThruFunction) { + if (type == null || this.getRawClassFor(type) == Void.class || this.target instanceof RoutingFunction + || this.isComposed() || this.target instanceof PassThruFunction) { if (this.getRawClassFor(type) == Void.class) { if (input instanceof Message) { input = ((Message) input).getPayload(); @@ -1137,6 +1178,7 @@ private boolean isInputConversionNecessary(Object input, Type type) { } return true; } + /* * */ @@ -1163,13 +1205,15 @@ else if (this.skipInputConversion) { if (!(input instanceof Message)) { input = MessageBuilder.withPayload(input).build(); } - convertedInput = this.isInputTypeMessage() - ? input + convertedInput = this.isInputTypeMessage() ? input : new OriginalMessageHolder(((Message) input).getPayload(), (Message) input); } else if (input instanceof Message) { input = this.filterOutHeaders((Message) input); - if (((Message) input).getPayload().getClass().getName().equals("org.springframework.kafka.support.KafkaNull")) { + if (((Message) input).getPayload() + .getClass() + .getName() + .equals("org.springframework.kafka.support.KafkaNull")) { return input; } @@ -1179,16 +1223,17 @@ else if (input instanceof Message) { convertedInput = this.convertInputMessageIfNecessary((Message) input, type); if (convertedInput == null) { // give ConversionService a chance - convertedInput = this.convertNonMessageInputIfNecessary(type, ((Message) input).getPayload(), false); + convertedInput = this.convertNonMessageInputIfNecessary(type, ((Message) input).getPayload(), + false); } if (convertedInput != null && !FunctionTypeUtils.isMultipleArgumentType(this.inputType)) { convertedInput = !convertedInput.equals(input) - ? new OriginalMessageHolder(convertedInput, (Message) input) - : convertedInput; + ? new OriginalMessageHolder(convertedInput, (Message) input) : convertedInput; } if (convertedInput != null && logger.isDebugEnabled()) { - logger.debug("Converted Message: " + input + " to: " + - (convertedInput instanceof OriginalMessageHolder ? ((OriginalMessageHolder) convertedInput).value.getClass() : convertedInput)); + logger.debug( + "Converted Message: " + input + " to: " + (convertedInput instanceof OriginalMessageHolder + ? ((OriginalMessageHolder) convertedInput).value.getClass() : convertedInput)); } } else { @@ -1207,7 +1252,8 @@ else if (input instanceof Message) { return convertedInput; } - // TODO temporary fix for https://github.com/spring-cloud/spring-cloud-stream/issues/2178 + // TODO temporary fix for + // https://github.com/spring-cloud/spring-cloud-stream/issues/2178 // need a cleaner solution @SuppressWarnings("unchecked") private Message filterOutHeaders(Message message) { @@ -1238,8 +1284,9 @@ private boolean isExtractPayload(Message message, Type type) { } /** - * This is an optional conversion which would only happen if `expected-content-type` is - * set as a header in a message or explicitly provided as part of the lookup. + * This is an optional conversion which would only happen if + * `expected-content-type` is set as a header in a message or explicitly provided + * as part of the lookup. */ @SuppressWarnings("unchecked") private Object convertOutputIfNecessary(Object output, Type type, String[] contentType) { @@ -1268,7 +1315,8 @@ else if (isExtractPayload((Message) convertedOutput, type)) { Message enrichedMessage; if (convertedOutput instanceof Message) { enrichedMessage = MessageBuilder.fromMessage((Message) convertedOutput) - .setHeader(MessageHeaders.CONTENT_TYPE, contentType[0]).build(); + .setHeader(MessageHeaders.CONTENT_TYPE, contentType[0]) + .build(); } else { enrichedMessage = MessageBuilder.withPayload(convertedOutput) @@ -1282,8 +1330,7 @@ else if (isExtractPayload((Message) convertedOutput, type)) { if (ObjectUtils.isEmpty(contentType)) { // GH-1307: For POJO functions, wrap output in Message to maintain // consistency with regular functions - if (this.isPojoFunction && output instanceof Message - && !(convertedOutput instanceof Message)) { + if (this.isPojoFunction && output instanceof Message && !(convertedOutput instanceof Message)) { convertedOutput = MessageBuilder.withPayload(convertedOutput) .copyHeaders(((Message) output).getHeaders()) .build(); @@ -1291,24 +1338,29 @@ else if (isExtractPayload((Message) convertedOutput, type)) { return convertedOutput; } - if (FunctionTypeUtils.isMultipleArgumentType(type)) { convertedOutput = this.convertMultipleOutputArgumentTypeIfNecesary(convertedOutput, type, contentType); } else if (convertedOutput instanceof Message) { - convertedOutput = this.convertOutputMessageIfNecessary(convertedOutput, ObjectUtils.isEmpty(contentType) ? null : contentType[0]); + convertedOutput = this.convertOutputMessageIfNecessary(convertedOutput, + ObjectUtils.isEmpty(contentType) ? null : contentType[0]); } else if (convertedOutput instanceof Collection && this.isOutputTypeMessage()) { - convertedOutput = this.convertMultipleOutputValuesIfNecessary(convertedOutput, ObjectUtils.isEmpty(contentType) ? null : contentType); + convertedOutput = this.convertMultipleOutputValuesIfNecessary(convertedOutput, + ObjectUtils.isEmpty(contentType) ? null : contentType); } else if (ObjectUtils.isArray(convertedOutput) && !(convertedOutput instanceof byte[])) { - convertedOutput = this.convertMultipleOutputValuesIfNecessary(convertedOutput, ObjectUtils.isEmpty(contentType) ? null : contentType); + convertedOutput = this.convertMultipleOutputValuesIfNecessary(convertedOutput, + ObjectUtils.isEmpty(contentType) ? null : contentType); } else { convertedOutput = messageConverter.toMessage(convertedOutput, - new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType == null ? "application/json" : contentType[0]))); + new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, + contentType == null ? "application/json" : contentType[0]))); if (FunctionTypeUtils.isTypeCollection(this.outputType) && output instanceof Message) { - convertedOutput = MessageBuilder.fromMessage((Message) convertedOutput).copyHeaders(((Message) output).getHeaders()).build(); + convertedOutput = MessageBuilder.fromMessage((Message) convertedOutput) + .copyHeaders(((Message) output).getHeaders()) + .build(); } } @@ -1316,15 +1368,16 @@ else if (ObjectUtils.isArray(convertedOutput) && !(convertedOutput instanceof by } /** - * Will check if message contains any of the headers that are considered to serve as - * signals to retain output as Message (regardless of the output type of function). - * At this moment presence of 'scf-func-name' header or any header that begins with `lambda' - * (use by AWS) will result in this method returning true. + * Will check if message contains any of the headers that are considered to serve + * as signals to retain output as Message (regardless of the output type of + * function). At this moment presence of 'scf-func-name' header or any header that + * begins with `lambda' (use by AWS) will result in this method returning true. */ /* - * TODO we need to investigate if this could be extracted into some type of strategy since at - * the pure core level there is no case for this to ever be true. In fact today it is only AWS Lambda - * case that requires it since it may contain forwarding url + * TODO we need to investigate if this could be extracted into some type of + * strategy since at the pure core level there is no case for this to ever be + * true. In fact today it is only AWS Lambda case that requires it since it may + * contain forwarding url */ private boolean containsRetainMessageSignalInHeaders(Message message) { if (functionInvocationHelper != null && functionInvocationHelper.isRetainOutputAsMessage(message)) { @@ -1332,8 +1385,7 @@ private boolean containsRetainMessageSignalInHeaders(Message message) { } else { for (String headerName : message.getHeaders().keySet()) { - if (headerName.startsWith("lambda") || - headerName.startsWith("scf-func-name")) { + if (headerName.startsWith("lambda") || headerName.startsWith("scf-func-name")) { return true; } } @@ -1358,8 +1410,7 @@ private Object convertNonMessageInputIfNecessary(Type inputType, Object input, b convertedInput = SimpleFunctionRegistry.this.jsonMapper.fromJson(input, inputType); } } - else if (SimpleFunctionRegistry.this.conversionService != null - && !rawInputType.equals(input.getClass()) + else if (SimpleFunctionRegistry.this.conversionService != null && !rawInputType.equals(input.getClass()) && SimpleFunctionRegistry.this.conversionService.canConvert(input.getClass(), rawInputType)) { convertedInput = SimpleFunctionRegistry.this.conversionService.convert(input, rawInputType); } @@ -1373,10 +1424,8 @@ else if (SimpleFunctionRegistry.this.conversionService != null * */ private boolean isWrapConvertedInputInMessage(Object convertedInput) { - return this.inputType != null - && FunctionTypeUtils.isMessage(this.inputType) - && !(convertedInput instanceof Message) - && !(convertedInput instanceof Publisher) + return this.inputType != null && FunctionTypeUtils.isMessage(this.inputType) + && !(convertedInput instanceof Message) && !(convertedInput instanceof Publisher) && !(convertedInput instanceof OriginalMessageHolder); } @@ -1384,7 +1433,8 @@ private boolean isWrapConvertedInputInMessage(Object convertedInput) { * */ private Type extractActualValueTypeIfNecessary(Type type) { - if (type instanceof ParameterizedType && (FunctionTypeUtils.isPublisher(type) || FunctionTypeUtils.isMessage(type))) { + if (type instanceof ParameterizedType + && (FunctionTypeUtils.isPublisher(type) || FunctionTypeUtils.isMessage(type))) { return FunctionTypeUtils.getGenericType(type); } return type; @@ -1420,35 +1470,39 @@ private Object convertInputMessageIfNecessary(Message message, Type type) { Object convertedInput = message.getPayload(); Type itemType = this.extractActualValueTypeIfNecessary(type); - Class rawType = FunctionTypeUtils.isMessage(type) - ? FunctionTypeUtils.getRawType(itemType) + Class rawType = FunctionTypeUtils.isMessage(type) ? FunctionTypeUtils.getRawType(itemType) : FunctionTypeUtils.getRawType(type); convertedInput = type instanceof ParameterizedType ? SimpleFunctionRegistry.this.messageConverter.fromMessage(message, rawType, itemType) : SimpleFunctionRegistry.this.messageConverter.fromMessage(message, rawType); if (convertedInput != null && !rawType.isAssignableFrom(convertedInput.getClass())) { - logger.warn("Failed to convert input to " + rawType + ". Will attempt to invoke function with raw type"); + logger + .warn("Failed to convert input to " + rawType + ". Will attempt to invoke function with raw type"); } if (FunctionTypeUtils.isMessage(type)) { if (convertedInput == null) { if (logger.isDebugEnabled()) { /* - * In the event conversion was unsuccessful we simply return the original un-converted message. - * This will help to deal with issues like KafkaNull and others. However if this was not the intention - * of the developer, this would be discovered early in the development process where the - * additional message converter could be added to facilitate the conversion. + * In the event conversion was unsuccessful we simply return the + * original un-converted message. This will help to deal with + * issues like KafkaNull and others. However if this was not the + * intention of the developer, this would be discovered early in + * the development process where the additional message converter + * could be added to facilitate the conversion. */ - logger.debug("Input type conversion of payload " + message.getPayload() + " resulted in 'null'. " - + "Will use the original message as input."); + logger.debug("Input type conversion of payload " + message.getPayload() + + " resulted in 'null'. " + "Will use the original message as input."); } convertedInput = message; } else { if (!(convertedInput instanceof Message)) { - convertedInput = MessageBuilder.withPayload(convertedInput).copyHeaders(message.getHeaders()).build(); + convertedInput = MessageBuilder.withPayload(convertedInput) + .copyHeaders(message.getHeaders()) + .build(); } } } @@ -1463,10 +1517,10 @@ private Object convertMultipleOutputArgumentTypeIfNecesary(Object output, Type t Object[] multipleValueArguments = this.parseMultipleValueArguments(output, outputTypes.length); Object[] convertedOutputs = new Object[outputTypes.length]; for (int i = 0; i < multipleValueArguments.length; i++) { - String[] ctToUse = !ObjectUtils.isEmpty(contentType) - ? new String[]{contentType[i]} - : new String[] {"application/json"}; - Object convertedInput = this.convertOutputIfNecessary(multipleValueArguments[i], outputTypes[i], ctToUse); + String[] ctToUse = !ObjectUtils.isEmpty(contentType) ? new String[] { contentType[i] } + : new String[] { "application/json" }; + Object convertedInput = this.convertOutputIfNecessary(multipleValueArguments[i], outputTypes[i], + ctToUse); convertedOutputs[i] = convertedInput; } return Tuples.fromArray(convertedOutputs); @@ -1478,13 +1532,14 @@ private Object convertMultipleOutputArgumentTypeIfNecesary(Object output, Type t @SuppressWarnings("unchecked") private Object convertOutputMessageIfNecessary(Object output, String expectedOutputContetntType) { String contentType; - if (this.isOutputTypeMessage() && ((Message) output).getHeaders().containsKey(MessageHeaders.CONTENT_TYPE)) { + 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; + : expectedOutputContetntType; } if (StringUtils.hasText(contentType)) { @@ -1492,7 +1547,9 @@ private Object convertOutputMessageIfNecessary(Object output, String expectedOut String[] expectedContentTypes = StringUtils.delimitedListToStringArray(contentType, ","); for (String expectedContentType : expectedContentTypes) { headersMap.put(MessageHeaders.CONTENT_TYPE, expectedContentType); - Message message = MessageBuilder.withPayload(((Message) output).getPayload()).copyHeaders(headersMap).build(); + Message message = MessageBuilder.withPayload(((Message) output).getPayload()) + .copyHeaders(headersMap) + .build(); Object result = messageConverter.toMessage(message.getPayload(), message.getHeaders()); if (result != null) { return result; @@ -1507,9 +1564,12 @@ private Object convertOutputMessageIfNecessary(Object output, String expectedOut */ @SuppressWarnings("unchecked") private Object convertMultipleOutputValuesIfNecessary(Object output, String[] contentType) { - Collection outputCollection = ObjectUtils.isArray(output) ? CollectionUtils.arrayToList(output) : (Collection) output; - Collection convertedOutputCollection = outputCollection instanceof List ? new ArrayList<>() : new TreeSet<>(); - Type type = this.isOutputTypeMessage() ? FunctionTypeUtils.getGenericType(this.outputType) : this.outputType; + Collection outputCollection = ObjectUtils.isArray(output) ? CollectionUtils.arrayToList(output) + : (Collection) output; + Collection convertedOutputCollection = outputCollection instanceof List ? new ArrayList<>() + : new TreeSet<>(); + Type type = this.isOutputTypeMessage() ? FunctionTypeUtils.getGenericType(this.outputType) + : this.outputType; for (Object outToConvert : outputCollection) { Object result = this.convertOutputIfNecessary(outToConvert, type, contentType); Assert.notNull(result, () -> "Failed to convert output '" + outToConvert + "'"); @@ -1530,56 +1590,54 @@ else if (FunctionTypeUtils.isFlux(type) && publisher instanceof Mono) { publisher = Flux.from(publisher); } Type actualType = type != null && FunctionTypeUtils.isPublisher(type) - ? FunctionTypeUtils.getImmediateGenericType(type, 0) - : type; - return publisher instanceof Mono - ? Mono.from(publisher).map(v -> { - try { - return this.convertInputIfNecessary(v, actualType == null ? type : actualType); - } - catch (Exception e) { - throw new IllegalStateException("Failed to convert input", e); - } - }) - : Flux.from(publisher).map(v -> { - try { - return this.convertInputIfNecessary(v, actualType == null ? type : actualType); - } - catch (Exception e) { - throw new IllegalStateException("Failed to convert input", e); - } - }); + ? FunctionTypeUtils.getImmediateGenericType(type, 0) : type; + return publisher instanceof Mono ? Mono.from(publisher).map(v -> { + try { + return this.convertInputIfNecessary(v, actualType == null ? type : actualType); + } + catch (Exception e) { + throw new IllegalStateException("Failed to convert input", e); + } + }) : Flux.from(publisher).map(v -> { + try { + return this.convertInputIfNecessary(v, actualType == null ? type : actualType); + } + catch (Exception e) { + throw new IllegalStateException("Failed to convert input", e); + } + }); } /* * */ @SuppressWarnings("unchecked") - private Object convertOutputPublisherIfNecessary(Publisher publisher, Type type, String[] expectedOutputContentType) { - return publisher instanceof Mono - ? Mono.from(publisher).map(v -> { - try { - return this.convertOutputIfNecessary(v, type, expectedOutputContentType); - } - catch (Exception e) { - throw new IllegalStateException("Failed to convert output", e); - } - }) - : Flux.from(publisher).map(v -> { - try { - return this.convertOutputIfNecessary(v, type, expectedOutputContentType); - } - catch (Exception e) { - throw new IllegalStateException("Failed to convert output", e); - } - }); + private Object convertOutputPublisherIfNecessary(Publisher publisher, Type type, + String[] expectedOutputContentType) { + return publisher instanceof Mono ? Mono.from(publisher).map(v -> { + try { + return this.convertOutputIfNecessary(v, type, expectedOutputContentType); + } + catch (Exception e) { + throw new IllegalStateException("Failed to convert output", e); + } + }) : Flux.from(publisher).map(v -> { + try { + return this.convertOutputIfNecessary(v, type, expectedOutputContentType); + } + catch (Exception e) { + throw new IllegalStateException("Failed to convert output", e); + } + }); } + } /** * */ - private static final class OriginalMessageHolder { + private static final class OriginalMessageHolder { + private final Object value; private final Message originalMessage; @@ -1596,13 +1654,16 @@ public Object getValue() { public Message getOriginalMessage() { return this.originalMessage; } + } public static class PassThruFunction implements Function { + @Override public Object apply(Object t) { return t; } + } @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -1620,4 +1681,5 @@ public void accept(Flux messageFlux) { } } + } 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 652e65221..827f61b6e 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 @@ -87,7 +87,6 @@ import org.springframework.util.MimeType; import org.springframework.util.StringUtils; - /** * @author Dave Syer * @author Mark Fisher @@ -99,19 +98,23 @@ */ @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(FunctionProperties.class) -@AutoConfigureAfter(name = {"org.springframework.cloud.function.deployer.FunctionDeployerConfiguration"}) +@AutoConfigureAfter(name = { "org.springframework.cloud.function.deployer.FunctionDeployerConfiguration" }) public class ContextFunctionCatalogAutoConfiguration { private static Log logger = LogFactory.getLog(ContextFunctionCatalogAutoConfiguration.class); + /** - * The name of the property to specify desired JSON mapper. Available values are `jackson' and 'gson'. + * The name of the property to specify desired JSON mapper. Available values are + * `jackson' and 'gson'. */ public static final String JSON_MAPPER_PROPERTY = "spring.cloud.function.preferred-json-mapper"; @Bean public FunctionRegistry functionCatalog(List messageConverters, JsonMapper jsonMapper, - ConfigurableApplicationContext context, @Nullable FunctionInvocationHelper> functionInvocationHelper) { - ConfigurableConversionService conversionService = (ConfigurableConversionService) context.getBeanFactory().getConversionService(); + ConfigurableApplicationContext context, + @Nullable FunctionInvocationHelper> functionInvocationHelper) { + ConfigurableConversionService conversionService = (ConfigurableConversionService) context.getBeanFactory() + .getConversionService(); if (conversionService == null) { conversionService = new DefaultConversionService(); } @@ -135,9 +138,7 @@ public FunctionRegistry functionCatalog(List messageConverters } } - mcList = mcList.stream() - .filter(this::isConverterEligible) - .collect(Collectors.toList()); + mcList = mcList.stream().filter(this::isConverterEligible).collect(Collectors.toList()); mcList.add(new JsonMessageConverter(jsonMapper)); mcList.add(new ByteArrayMessageConverter()); @@ -166,20 +167,24 @@ public MimeType resolve(MessageHeaders headers) throws InvalidMimeTypeException ((CloudEventsFunctionInvocationHelper) functionInvocationHelper).setMessageConverter(messageConverter); } FunctionProperties functionProperties = context.getBean(FunctionProperties.class); - return new BeanFactoryAwareFunctionRegistry(conversionService, messageConverter, jsonMapper, functionProperties, functionInvocationHelper); + return new BeanFactoryAwareFunctionRegistry(conversionService, messageConverter, jsonMapper, functionProperties, + functionInvocationHelper); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Bean(RoutingFunction.FUNCTION_NAME) public RoutingFunction functionRouter(FunctionCatalog functionCatalog, FunctionProperties functionProperties, - BeanFactory beanFactory, @Nullable MessageRoutingCallback routingCallback, - @Nullable DefaultMessageRoutingHandler defaultMessageRoutingHandler) { + BeanFactory beanFactory, @Nullable MessageRoutingCallback routingCallback, + @Nullable DefaultMessageRoutingHandler defaultMessageRoutingHandler) { if (defaultMessageRoutingHandler != null) { - FunctionRegistration functionRegistration = new FunctionRegistration(defaultMessageRoutingHandler, RoutingFunction.DEFAULT_ROUTE_HANDLER); - functionRegistration.type(FunctionTypeUtils.consumerType(ResolvableType.forClassWithGenerics(Message.class, Object.class).getType())); + FunctionRegistration functionRegistration = new FunctionRegistration(defaultMessageRoutingHandler, + RoutingFunction.DEFAULT_ROUTE_HANDLER); + functionRegistration.type(FunctionTypeUtils + .consumerType(ResolvableType.forClassWithGenerics(Message.class, Object.class).getType())); ((FunctionRegistry) functionCatalog).register(functionRegistration); } - return new RoutingFunction(functionCatalog, functionProperties, new BeanFactoryResolver(beanFactory), routingCallback); + return new RoutingFunction(functionCatalog, functionProperties, new BeanFactoryResolver(beanFactory), + routingCallback); } private boolean isConverterEligible(Object messageConverter) { @@ -199,19 +204,23 @@ static class CloudEventsMessageConverterConfiguration { public CloudEventMessageConverter cloudEventMessageConverter() { return new CloudEventMessageConverter(); } + } @ComponentScan(basePackages = "${spring.cloud.function.scan.packages:functions}", - includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { Supplier.class, Function.class, Consumer.class }), - excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = { Configuration.class, Component.class})) + includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, + classes = { Supplier.class, Function.class, Consumer.class }), + excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = { Configuration.class, Component.class })) @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(prefix = "spring.cloud.function.scan", name = "enabled", havingValue = "true", matchIfMissing = true) + @ConditionalOnProperty(prefix = "spring.cloud.function.scan", name = "enabled", havingValue = "true", + matchIfMissing = true) protected static class PlainFunctionScanConfiguration { } @Configuration(proxyBeanMethods = false) public static class JsonMapperConfiguration { + @Bean @ConditionalOnMissingBean(JsonMapper.class) public JsonMapper jsonMapper(ApplicationContext context) { @@ -232,12 +241,13 @@ else if (ClassUtils.isPresent("com.google.gson.Gson", null)) { return gson(context); } } - throw new IllegalStateException("Failed to configure JsonMapper. Neither jackson nor gson are present on the claspath"); + throw new IllegalStateException( + "Failed to configure JsonMapper. Neither jackson nor gson are present on the claspath"); } private JsonMapper gson(ApplicationContext context) { Assert.state(ClassUtils.isPresent("com.google.gson.Gson", ClassUtils.getDefaultClassLoader()), - "Can not bootstrap Gson mapper since Gson is not on the classpath"); + "Can not bootstrap Gson mapper since Gson is not on the classpath"); Gson gson; try { gson = context.getBean(Gson.class); @@ -250,8 +260,9 @@ private JsonMapper gson(ApplicationContext context) { @SuppressWarnings("unchecked") private JsonMapper jackson(ApplicationContext context) { - Assert.state(ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", ClassUtils.getDefaultClassLoader()), - "Can not bootstrap Jackson mapper since Jackson is not on the classpath"); + Assert.state( + ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", ClassUtils.getDefaultClassLoader()), + "Can not bootstrap Jackson mapper since Jackson is not on the classpath"); ObjectMapper mapper = null; MapperBuilder builder = tools.jackson.databind.json.JsonMapper.builder(); try { @@ -265,8 +276,9 @@ private JsonMapper jackson(ApplicationContext context) { if (KotlinDetector.isKotlinPresent()) { try { - Class kotlinModuleClass = (Class) - ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", ClassUtils.getDefaultClassLoader()); + Class kotlinModuleClass = (Class) ClassUtils + .forName("com.fasterxml.jackson.module.kotlin.KotlinModule", + ClassUtils.getDefaultClassLoader()); JacksonModule kotlinModule = BeanUtils.instantiateClass(kotlinModuleClass); builder = builder.addModule(kotlinModule); } @@ -282,5 +294,7 @@ private JsonMapper jackson(ApplicationContext context) { mapper = builder.build(); return new JacksonMapper(mapper); } + } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java index e3de40732..175220752 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java @@ -117,14 +117,14 @@ protected void register(BeanDefinitionRegistry registry, ConfigurableListableBea performPreinitialization(); - if (this.context.getBeanFactory().getBeanNamesForType(PropertySourcesPlaceholderConfigurer.class, false, - false).length == 0) { + if (this.context.getBeanFactory() + .getBeanNamesForType(PropertySourcesPlaceholderConfigurer.class, false, false).length == 0) { this.context.registerBean(PropertySourcesPlaceholderConfigurer.class, - PropertySourcesPlaceholderConfigurer::new); + PropertySourcesPlaceholderConfigurer::new); } if (!this.context.getBeanFactory() - .containsBeanDefinition(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { + .containsBeanDefinition(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { // Switch off the ConfigurationClassPostProcessor this.context.registerBean(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME, DummyProcessor.class, () -> new DummyProcessor()); @@ -133,24 +133,29 @@ protected void register(BeanDefinitionRegistry registry, ConfigurableListableBea } ConfigurationPropertiesBindingPostProcessor.register(registry); - String preferredMapper = context.getEnvironment().getProperty(ContextFunctionCatalogAutoConfiguration.JSON_MAPPER_PROPERTY); + String preferredMapper = context.getEnvironment() + .getProperty(ContextFunctionCatalogAutoConfiguration.JSON_MAPPER_PROPERTY); if (ClassUtils.isPresent("com.google.gson.Gson", null) && "gson".equals(preferredMapper)) { if (this.context.getBeanFactory().getBeanNamesForType(Gson.class, false, false).length == 0) { this.context.registerBean(Gson.class, () -> new Gson()); } - this.context.registerBean(JsonMapper.class, () -> new ContextFunctionCatalogAutoConfiguration.JsonMapperConfiguration().jsonMapper(this.context)); + this.context.registerBean(JsonMapper.class, + () -> new ContextFunctionCatalogAutoConfiguration.JsonMapperConfiguration() + .jsonMapper(this.context)); } else if (ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", null)) { if (this.context.getBeanFactory().getBeanNamesForType(ObjectMapper.class, false, false).length == 0) { this.context.registerBean(ObjectMapper.class, () -> new ObjectMapper()); } - this.context.registerBean(JsonMapper.class, () -> new ContextFunctionCatalogAutoConfiguration.JsonMapperConfiguration().jsonMapper(this.context)); + this.context.registerBean(JsonMapper.class, + () -> new ContextFunctionCatalogAutoConfiguration.JsonMapperConfiguration() + .jsonMapper(this.context)); } - String basePackage = this.context.getEnvironment().getProperty("spring.cloud.function.scan.packages", - "functions"); + String basePackage = this.context.getEnvironment() + .getProperty("spring.cloud.function.scan.packages", "functions"); if (this.context.getEnvironment().getProperty("spring.cloud.function.scan.enabled", Boolean.class, true) && new ClassPathResource(basePackage.replace(".", "/")).exists()) { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.context, false, @@ -178,15 +183,17 @@ && new ClassPathResource(basePackage.replace(".", "/")).exists()) { messageConverters.add(new StringMessageConverter()); messageConverters.add(new PrimitiveTypesFromStringMessageConverter(new DefaultConversionService())); - SmartCompositeMessageConverter messageConverter = new SmartCompositeMessageConverter(messageConverters); + SmartCompositeMessageConverter messageConverter = new SmartCompositeMessageConverter( + messageConverters); ConversionService conversionService = new DefaultConversionService(); - return new SimpleFunctionRegistry(conversionService, messageConverter, this.context.getBean(JsonMapper.class)); + return new SimpleFunctionRegistry(conversionService, messageConverter, + this.context.getBean(JsonMapper.class)); }); this.context.registerBean(FunctionProperties.class, () -> new FunctionProperties()); - this.context.registerBean(FunctionRegistrationPostProcessor.class, - () -> new FunctionRegistrationPostProcessor(this.context.getAutowireCapableBeanFactory() - .getBeanProvider(FunctionRegistration.class))); + this.context + .registerBean(FunctionRegistrationPostProcessor.class, () -> new FunctionRegistrationPostProcessor( + this.context.getAutowireCapableBeanFactory().getBeanProvider(FunctionRegistration.class))); } } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java index 7a36140d5..82a641d59 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java @@ -59,7 +59,6 @@ else if (registry.containsBean(name)) { Class beanClass = null; - if (definition == null) { return null; } @@ -71,7 +70,8 @@ else if (registry.containsBean(name)) { Type param = null; if (source instanceof MethodMetadata) { - param = findBeanType(definition, ((MethodMetadata) source).getDeclaringClassName(), ((MethodMetadata) source).getMethodName()); + param = findBeanType(definition, ((MethodMetadata) source).getDeclaringClassName(), + ((MethodMetadata) source).getMethodName()); } else if (source instanceof Resource) { param = registry.getType(actualName); @@ -87,8 +87,8 @@ else if (source instanceof Resource) { return param; } - public static Class[] getParamTypesFromBeanDefinitionFactory(Class factory, - AbstractBeanDefinition definition, String methodName) { + public static Class[] getParamTypesFromBeanDefinitionFactory(Class factory, AbstractBeanDefinition definition, + String methodName) { if (definition instanceof RootBeanDefinition) { RootBeanDefinition root = (RootBeanDefinition) definition; for (Method method : getCandidateMethods(factory, root)) { @@ -98,8 +98,9 @@ public static Class[] getParamTypesFromBeanDefinitionFactory(Class factory } } List> params = new ArrayList<>(); - for (ConstructorArgumentValues.ValueHolder holder : definition - .getConstructorArgumentValues().getIndexedArgumentValues().values()) { + for (ConstructorArgumentValues.ValueHolder holder : definition.getConstructorArgumentValues() + .getIndexedArgumentValues() + .values()) { params.add(ClassUtils.resolveClassName(holder.getType(), null)); } return params.toArray(new Class[0]); @@ -107,7 +108,8 @@ public static Class[] getParamTypesFromBeanDefinitionFactory(Class factory private static Class resolveBeanClass(AbstractBeanDefinition beanDefinition) { try { - return beanDefinition.hasBeanClass() ? beanDefinition.getBeanClass() : ClassUtils.getDefaultClassLoader().loadClass(beanDefinition.getBeanClassName()); + return beanDefinition.hasBeanClass() ? beanDefinition.getBeanClass() + : ClassUtils.getDefaultClassLoader().loadClass(beanDefinition.getBeanClassName()); } catch (Exception e) { return null; @@ -117,16 +119,14 @@ private static Class resolveBeanClass(AbstractBeanDefinition beanDefinition) private static Type findBeanType(AbstractBeanDefinition definition, String declaringClassName, String methodName) { Class factory = ClassUtils.resolveClassName(declaringClassName, null); Class[] params = getParamTypesFromBeanDefinitionFactory(factory, definition, methodName); - Method method = ReflectionUtils.findMethod(factory, methodName, - params); + Method method = ReflectionUtils.findMethod(factory, methodName, params); Type type = method.getGenericReturnType(); return type; } - private static Method[] getCandidateMethods(final Class factoryClass, - final RootBeanDefinition mbd) { - return (mbd.isNonPublicAccessAllowed() - ? ReflectionUtils.getAllDeclaredMethods(factoryClass) + private static Method[] getCandidateMethods(final Class factoryClass, final RootBeanDefinition mbd) { + return (mbd.isNonPublicAccessAllowed() ? ReflectionUtils.getAllDeclaredMethods(factoryClass) : factoryClass.getMethods()); } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java index d734d4a14..83e6487b2 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java @@ -31,8 +31,7 @@ * @since 3.2 */ @Configuration(proxyBeanMethods = false) -@ConditionalOnClass(name = { - "org.springframework.boot.actuate.endpoint.annotation.Endpoint" }) +@ConditionalOnClass(name = { "org.springframework.boot.actuate.endpoint.annotation.Endpoint" }) @ConditionalOnBean(FunctionCatalog.class) @AutoConfigureAfter(EndpointAutoConfiguration.class) public class FunctionsEndpointAutoConfiguration { diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/JsonMessageConverter.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/JsonMessageConverter.java index 8747e3b8f..15652bc9f 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/JsonMessageConverter.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/JsonMessageConverter.java @@ -34,12 +34,11 @@ import org.springframework.util.StringUtils; /** - * Implementation of {@link MessageConverter} which uses Jackson or Gson libraries to do the - * actual conversion via {@link JsonMapper} instance. + * Implementation of {@link MessageConverter} which uses Jackson or Gson libraries to do + * the actual conversion via {@link JsonMapper} instance. * * @author Oleg Zhurakousky * @author Andrey Shlykov - * * @since 3.0.4 */ public class JsonMessageConverter extends AbstractMessageConverter { @@ -47,8 +46,9 @@ public class JsonMessageConverter extends AbstractMessageConverter { private final JsonMapper jsonMapper; public JsonMessageConverter(JsonMapper jsonMapper) { - this(jsonMapper, new MimeType("application", "json"), new MimeType(CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getType(), - CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getSubtype() + "+json")); + this(jsonMapper, new MimeType("application", "json"), + new MimeType(CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getType(), + CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getSubtype() + "+json")); } public JsonMessageConverter(JsonMapper jsonMapper, MimeType... supportedMimeTypes) { @@ -94,7 +94,9 @@ protected Object convertFromInternal(Message message, Class targetClass, @ convertToType = Thread.currentThread().getContextClassLoader().loadClass(type); } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Failed to load class `" + type + "` specified by the provided content-type: " + mimeType, e); + throw new IllegalArgumentException( + "Failed to load class `" + type + "` specified by the provided content-type: " + mimeType, + e); } } else { 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 8c9419300..f9e742960 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 @@ -57,13 +57,11 @@ public class KotlinLambdaToFunctionAutoConfiguration { protected final Log logger = LogFactory.getLog(getClass()); - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static final class KotlinFunctionWrapper implements Function, Supplier, Consumer, - Function0, Function1, Function2, + public static final class KotlinFunctionWrapper implements Function, Supplier, + Consumer, Function0, Function1, Function2, Function3, Function4 { - private final Object kotlinLambdaTarget; private String name; @@ -145,8 +143,7 @@ public Object get() { public FunctionRegistration getFunctionRegistration() { String name = this.name.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) - ? this.name.replace(FunctionRegistration.REGISTRATION_NAME_SUFFIX, "") - : this.name; + ? this.name.replace(FunctionRegistration.REGISTRATION_NAME_SUFFIX, "") : this.name; Type functionType = FunctionContextUtils.findType(name, this.beanFactory); FunctionRegistration registration = new FunctionRegistration<>(this, name); Type[] types = ((ParameterizedType) functionType).getActualTypeArguments(); @@ -160,34 +157,36 @@ else if (isValidKotlinConsumer(functionType, types)) { .getType(); } else if (isValidKotlinFunction(functionType, types)) { - functionType = ResolvableType.forClassWithGenerics(Function.class, ResolvableType.forType(types[0]), - ResolvableType.forType(types[1])).getType(); + functionType = ResolvableType + .forClassWithGenerics(Function.class, ResolvableType.forType(types[0]), + ResolvableType.forType(types[1])) + .getType(); } else if (isValidKotlinSuspendSupplier(functionType, types)) { Type continuationReturnType = CoroutinesUtils.getSuspendingFunctionReturnType(types[0]); - functionType = ResolvableType.forClassWithGenerics( - Supplier.class, - ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationReturnType)) - ).getType(); + functionType = ResolvableType + .forClassWithGenerics(Supplier.class, + ResolvableType.forClassWithGenerics(Flux.class, + ResolvableType.forType(continuationReturnType))) + .getType(); } else if (isValidKotlinSuspendFunction(functionType, types)) { Type continuationArgType = CoroutinesUtils.getSuspendingFunctionArgType(types[0]); Type continuationReturnType = CoroutinesUtils.getSuspendingFunctionReturnType(types[1]); - functionType = ResolvableType.forClassWithGenerics( - Function.class, - ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationArgType)), - ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationReturnType)) - ).getType(); + functionType = ResolvableType.forClassWithGenerics(Function.class, + ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationArgType)), + ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationReturnType))) + .getType(); } else if (isValidKotlinSuspendConsumer(functionType, types)) { Type continuationArgType = CoroutinesUtils.getSuspendingFunctionArgType(types[0]); - functionType = ResolvableType.forClassWithGenerics( - Consumer.class, - ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationArgType)) - ).getType(); + functionType = ResolvableType + .forClassWithGenerics(Consumer.class, + ResolvableType.forClassWithGenerics(Flux.class, + ResolvableType.forType(continuationArgType))) + .getType(); } - else if (!FunctionTypeUtils.isFunction(functionType) - && !FunctionTypeUtils.isConsumer(functionType) + else if (!FunctionTypeUtils.isFunction(functionType) && !FunctionTypeUtils.isConsumer(functionType) && !FunctionTypeUtils.isSupplier(functionType)) { throw new UnsupportedOperationException("Multi argument Kotlin functions are not currently supported"); } @@ -200,36 +199,28 @@ private boolean isValidKotlinSupplier(Type functionType) { } private boolean isValidKotlinConsumer(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function1.class) && - type.length == 2 && - !CoroutinesUtils.isContinuationType(type[0]) && - isTypeRepresentedByClass(type[1], Unit.class); + return isTypeRepresentedByClass(functionType, Function1.class) && type.length == 2 + && !CoroutinesUtils.isContinuationType(type[0]) && isTypeRepresentedByClass(type[1], Unit.class); } private boolean isValidKotlinFunction(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function1.class) && - type.length == 2 && - !CoroutinesUtils.isContinuationType(type[0]) && - !isTypeRepresentedByClass(type[1], Unit.class); + return isTypeRepresentedByClass(functionType, Function1.class) && type.length == 2 + && !CoroutinesUtils.isContinuationType(type[0]) && !isTypeRepresentedByClass(type[1], Unit.class); } private boolean isValidKotlinSuspendSupplier(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function1.class) && - type.length == 2 && - CoroutinesUtils.isContinuationFlowType(type[0]); + return isTypeRepresentedByClass(functionType, Function1.class) && type.length == 2 + && CoroutinesUtils.isContinuationFlowType(type[0]); } private boolean isValidKotlinSuspendConsumer(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function2.class) && - type.length == 3 && - CoroutinesUtils.isFlowType(type[0]) && - CoroutinesUtils.isContinuationUnitType(type[1]); + return isTypeRepresentedByClass(functionType, Function2.class) && type.length == 3 + && CoroutinesUtils.isFlowType(type[0]) && CoroutinesUtils.isContinuationUnitType(type[1]); } private boolean isValidKotlinSuspendFunction(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function2.class) && - type.length == 3 && - CoroutinesUtils.isContinuationFlowType(type[1]); + return isTypeRepresentedByClass(functionType, Function2.class) && type.length == 3 + && CoroutinesUtils.isContinuationFlowType(type[1]); } private boolean isTypeRepresentedByClass(Type type, Class clazz) { @@ -240,14 +231,14 @@ public Class getObjectType() { return FunctionRegistration.class; } - public void setName(String name) { this.name = name; } - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } + } + } 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 index a0293efbc..06a7517cb 100644 --- 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 @@ -24,9 +24,9 @@ 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. - * + * 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. */ @@ -34,11 +34,10 @@ default boolean shouldFailIfCantConvert(Message message) { return false; } - /** - * 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. - * + * 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 * @param t exception (coudl be null) * @return true if conversion failure must be considered fatal. @@ -51,12 +50,13 @@ default boolean shouldFailIfCantConvert(Message message, Throwable t) { } /** - * 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. - * + * 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 62042a8ff..dfad78983 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 @@ -53,7 +53,7 @@ * @since 2.1 * */ -//TODO - perhaps change to Function, Message> +// TODO - perhaps change to Function, Message> public class RoutingFunction implements Function { /** @@ -71,7 +71,8 @@ public class RoutingFunction implements Function { private final StandardEvaluationContext evalContext = new StandardEvaluationContext(); private final SimpleEvaluationContext headerEvalContext = SimpleEvaluationContext - .forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess()).build(); + .forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess()) + .build(); private final SpelExpressionParser spelParser = new SpelExpressionParser(); @@ -111,19 +112,15 @@ public Object apply(Object input) { return this.route(input, input instanceof Publisher); } - /* - * - Check if `this.routingCallback` is present and if it is use it (only for Message input) - * If NOT - * - Check if spring.cloud.function.definition is set in header and if it is use it.(only for Message input) - * If NOT - * - Check if spring.cloud.function.routing-expression is set in header and if it is set use it (only for Message input) - * If NOT - * - Check `spring.cloud.function.definition` is set in FunctionProperties and if it is use it (Message and Publisher) - * If NOT - * - Check `spring.cloud.function.routing-expression` is set in FunctionProperties and if it is use it (Message and Publisher) - * If NOT - * - Fail + * - Check if `this.routingCallback` is present and if it is use it (only for Message + * input) If NOT - Check if spring.cloud.function.definition is set in header and if + * it is use it.(only for Message input) If NOT - Check if + * spring.cloud.function.routing-expression is set in header and if it is set use it + * (only for Message input) If NOT - Check `spring.cloud.function.definition` is set + * in FunctionProperties and if it is use it (Message and Publisher) If NOT - Check + * `spring.cloud.function.routing-expression` is set in FunctionProperties and if it + * is use it (Message and Publisher) If NOT - Fail */ private Object route(Object input, boolean originalInputIsPublisher) { FunctionInvocationWrapper function = null; @@ -151,7 +148,8 @@ else if (StringUtils.hasText(functionProperties.getDefinition())) { else { throw new IllegalStateException("Failed to establish route, since neither were provided: " + "'spring.cloud.function.definition' as Message header or as application property or " - + "'spring.cloud.function.routing-expression' as application property. Incoming message: " + input); + + "'spring.cloud.function.routing-expression' as application property. Incoming message: " + + input); } } } @@ -163,9 +161,8 @@ else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { function = this.functionFromExpression(functionProperties.getRoutingExpression(), input); } else { - return input instanceof Mono mono - ? Mono.from(mono).map(v -> route(v, originalInputIsPublisher)) - : Flux.from(publisher).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 { @@ -173,8 +170,7 @@ else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { if (StringUtils.hasText(functionProperties.getRoutingExpression())) { function = this.functionFromExpression(functionProperties.getRoutingExpression(), input); } - else - if (StringUtils.hasText(functionProperties.getDefinition())) { + else if (StringUtils.hasText(functionProperties.getDefinition())) { function = functionFromDefinition(functionProperties.getDefinition()); } else { @@ -185,9 +181,10 @@ else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { } 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."); + 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."); } return function.apply(input); @@ -210,7 +207,8 @@ private FunctionInvocationWrapper locateFunctionFromDefinitionOrExpression(Messa return functionFromDefinition(definition); } else if (headerValue instanceof List definitions && !definitions.isEmpty()) { - return functionFromDefinition(definitions.stream().map(Object::toString).collect(Collectors.joining(","))); + return functionFromDefinition( + definitions.stream().map(Object::toString).collect(Collectors.joining(","))); } } else if (isRoutingExpression) { @@ -233,8 +231,9 @@ private void assertOriginalInputIsNotPublisher(boolean originalInputIsPublisher) private FunctionInvocationWrapper functionFromDefinition(String definition) { FunctionInvocationWrapper function = this.resolveFunction(definition); - Assert.notNull(function, "Failed to lookup function to route based on the value of 'spring.cloud.function.definition' property '" - + definition + "'"); + Assert.notNull(function, + "Failed to lookup function to route based on the value of 'spring.cloud.function.definition' property '" + + definition + "'"); if (logger.isDebugEnabled()) { logger.debug("Resolved function from provided [definition] property " + definition); } @@ -245,17 +244,22 @@ private FunctionInvocationWrapper functionFromExpression(String routingExpressio return functionFromExpression(routingExpression, input, false); } - private FunctionInvocationWrapper functionFromExpression(String routingExpression, Object input, boolean isViaHeader) { + private FunctionInvocationWrapper functionFromExpression(String routingExpression, Object input, + boolean isViaHeader) { Expression expression = spelParser.parseExpression(routingExpression); if (input instanceof Message) { input = MessageUtils.toCaseInsensitiveHeadersStructure((Message) input); } - String definition = isViaHeader ? expression.getValue(this.headerEvalContext, input, String.class) : expression.getValue(this.evalContext, input, String.class); - Assert.hasText(definition, "Failed to resolve function name based on routing expression '" + functionProperties.getRoutingExpression() + "'"); + String definition = isViaHeader ? expression.getValue(this.headerEvalContext, input, String.class) + : expression.getValue(this.evalContext, input, String.class); + Assert.hasText(definition, "Failed to resolve function name based on routing expression '" + + functionProperties.getRoutingExpression() + "'"); FunctionInvocationWrapper function = this.resolveFunction(definition); - Assert.notNull(function, "Failed to lookup function to route to based on the expression '" - + functionProperties.getRoutingExpression() + "' which resolved to '" + definition + "' function definition."); + Assert.notNull(function, + "Failed to lookup function to route to based on the expression '" + + functionProperties.getRoutingExpression() + "' which resolved to '" + definition + + "' function definition."); if (logger.isDebugEnabled()) { logger.debug("Resolved function from provided [routing-expression] " + routingExpression); } 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 7aa2266fe..9c729a0ea 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 @@ -42,7 +42,6 @@ import org.springframework.util.StringUtils; /** - * * @author Oleg Zhurakousky * @author Salvatore Bernardo * @@ -57,7 +56,8 @@ public SmartCompositeMessageConverter(Collection converters) { this(converters, null); } - public SmartCompositeMessageConverter(Collection converters, Supplier> messageConverterHelpersSupplier) { + public SmartCompositeMessageConverter(Collection converters, + Supplier> messageConverterHelpersSupplier) { super(converters); this.messageConverterHelpersSupplier = messageConverterHelpersSupplier; } @@ -68,7 +68,8 @@ 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)) { + if (!(message.getPayload() instanceof byte[]) && targetClass.isInstance(message.getPayload()) + && !(message.getPayload() instanceof Collection)) { return message.getPayload(); } try { @@ -93,7 +94,8 @@ public Object fromMessage(Message message, Class targetClass) { 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)) { + if (!(message.getPayload() instanceof byte[]) && targetClass.isInstance(message.getPayload()) + && !(message.getPayload() instanceof Collection)) { return message.getPayload(); } Object result = null; @@ -108,13 +110,27 @@ public Object fromMessage(Message message, Class targetClass, @Nullable Ob resultList.add(null); isConverted = true; } - for (Iterator iterator = getConverters().iterator(); iterator.hasNext() && !isConverted;) { + for (Iterator iterator = getConverters().iterator(); iterator.hasNext() + && !isConverted;) { MessageConverter converter = (MessageConverter) iterator.next(); - if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { // TODO Stream stuff, needs to be removed - Message m = MessageBuilder.withPayload(item).copyHeaders(message.getHeaders()).build(); // TODO Message creating may be expensive - Object conversionResult = (converter instanceof SmartMessageConverter & genericItemRawType != genericItemType ? - ((SmartMessageConverter) converter).fromMessage(m, genericItemRawType, genericItemType) : - converter.fromMessage(m, genericItemRawType)); + if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { // TODO + // Stream + // stuff, + // needs + // to + // be + // removed + Message m = MessageBuilder.withPayload(item).copyHeaders(message.getHeaders()).build(); // TODO + // Message + // creating + // may + // be + // expensive + Object conversionResult = (converter instanceof SmartMessageConverter + & genericItemRawType != genericItemType + ? ((SmartMessageConverter) converter).fromMessage(m, genericItemRawType, + genericItemType) + : converter.fromMessage(m, genericItemRawType)); if (conversionResult != null) { resultList.add(conversionResult); isConverted = true; @@ -130,10 +146,16 @@ public Object fromMessage(Message message, Class targetClass, @Nullable Ob } else { for (MessageConverter converter : getConverters()) { - if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { // TODO Stream stuff, needs to be removed - result = (converter instanceof SmartMessageConverter ? - ((SmartMessageConverter) converter).fromMessage(message, targetClass, conversionHint) : - converter.fromMessage(message, targetClass)); + if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { // TODO + // Stream + // stuff, + // needs + // to + // be + // removed + result = (converter instanceof SmartMessageConverter + ? ((SmartMessageConverter) converter).fromMessage(message, targetClass, conversionHint) + : converter.fromMessage(message, targetClass)); if (result != null) { return result; } @@ -144,7 +166,8 @@ public Object fromMessage(Message message, Class targetClass, @Nullable Ob return result; } - private void failConversionIfNecessary(Message message, Collection messageConverterHelpers, Throwable t) { + private void failConversionIfNecessary(Message message, + Collection messageConverterHelpers, Throwable t) { for (MessageConverterHelper messageConverterHelper : messageConverterHelpers) { if (messageConverterHelper.shouldFailIfCantConvert(message, t)) { throw new MessageConversionException("Failed to convert Message: " + message @@ -153,7 +176,8 @@ private void failConversionIfNecessary(Message message, Collection message, Collection messageConverterHelpers, int index) { + private void postProcessBatchMessage(Message message, Collection messageConverterHelpers, + int index) { for (MessageConverterHelper messageConverterHelper : messageConverterHelpers) { messageConverterHelper.postProcessBatchMessageOnFailure(message, index); } @@ -172,7 +196,8 @@ public Message toMessage(Object payload, @Nullable MessageHeaders headers) { for (String contentType : contentTypes) { if (!MimeType.valueOf(contentType).isConcrete()) { if (converter instanceof AbstractMessageConverter) { - List supportedMimeTypes = ((AbstractMessageConverter) converter).getSupportedMimeTypes(); + List supportedMimeTypes = ((AbstractMessageConverter) converter) + .getSupportedMimeTypes(); for (MimeType supportedMimeType : supportedMimeTypes) { if (supportedMimeType.isCompatibleWith(MimeType.valueOf(contentType))) { MessageHeaderAccessor h = new MessageHeaderAccessor(); @@ -213,7 +238,8 @@ public Message toMessage(Object payload, @Nullable MessageHeaders headers, @N MessageHeaderAccessor h = new MessageHeaderAccessor(); h.copyHeaders(headers); h.setHeader(MessageHeaders.CONTENT_TYPE, supportedMimeType); - Message result = ((SmartMessageConverter) converter).toMessage(payload, h.getMessageHeaders(), conversionHint); + Message result = ((SmartMessageConverter) converter).toMessage(payload, + h.getMessageHeaders(), conversionHint); if (result != null) { return result; } @@ -223,7 +249,8 @@ public Message toMessage(Object payload, @Nullable MessageHeaders headers, @N MessageHeaderAccessor h = new MessageHeaderAccessor(); h.copyHeaders(headers); h.setHeader(MessageHeaders.CONTENT_TYPE, contentType); - Message result = ((SmartMessageConverter) converter).toMessage(payload, h.getMessageHeaders(), conversionHint); + Message result = ((SmartMessageConverter) converter).toMessage(payload, h.getMessageHeaders(), + conversionHint); if (result != null) { return result; } @@ -232,4 +259,5 @@ public Message toMessage(Object payload, @Nullable MessageHeaders headers, @N } return null; } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/message/MessageUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/message/MessageUtils.java index 0d073b4d0..10fddb685 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/message/MessageUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/message/MessageUtils.java @@ -31,13 +31,15 @@ public abstract class MessageUtils { * Value for 'message-type' typically use as header key. */ public static String MESSAGE_TYPE = "message-type"; + /** * Value for 'target-protocol' typically use as header key. */ public static String SOURCE_TYPE = "source-type"; /** - * Returns (payload, headers) structure identical to `message` while substituting headers with case insensitive map. + * Returns (payload, headers) structure identical to `message` while substituting + * headers with case insensitive map. */ public static MessageStructureWithCaseInsensitiveHeaderKeys toCaseInsensitiveHeadersStructure(Message message) { return new MessageStructureWithCaseInsensitiveHeaderKeys(message); @@ -46,9 +48,11 @@ public static MessageStructureWithCaseInsensitiveHeaderKeys toCaseInsensitiveHea /** * !!! INTERNAL USE ONLY, MAY CHANGE OR REMOVED WITHOUT NOTICE!!! */ - @SuppressWarnings({"rawtypes"}) + @SuppressWarnings({ "rawtypes" }) public static class MessageStructureWithCaseInsensitiveHeaderKeys { + private final Object payload; + private final Map headers; @SuppressWarnings("unchecked") @@ -57,6 +61,7 @@ public static class MessageStructureWithCaseInsensitiveHeaderKeys { this.headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); this.headers.putAll(message.getHeaders()); } + public Object getPayload() { return payload; } @@ -64,5 +69,7 @@ public Object getPayload() { public Map getHeaders() { return headers; } + } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalSpringBootTest.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalSpringBootTest.java index 9f2059d3b..751a935a0 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalSpringBootTest.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalSpringBootTest.java @@ -29,7 +29,6 @@ import org.springframework.test.context.ContextConfiguration; /** - * * @author Dave Syer * @since 2.0 * diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java index b31e13e7e..7f455871d 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java @@ -27,7 +27,6 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.type.TypeFactory; - /** * @author Dave Syer * @author Oleg Zhurakousky @@ -70,7 +69,8 @@ else if (json instanceof Map) { } } catch (Exception e) { - throw new IllegalStateException("Failed to convert. Possible bug as the conversion probably shouldn't have been attempted here", e); + throw new IllegalStateException( + "Failed to convert. Possible bug as the conversion probably shouldn't have been attempted here", e); } return convertedValue; } @@ -101,6 +101,4 @@ public String toString(Object value) { } } - - } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java index a4ea34fa7..5b21502bc 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java @@ -34,7 +34,6 @@ import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; - /** * @author Dave Syer * @author Oleg Zhurakousky @@ -45,8 +44,8 @@ public abstract class JsonMapper { // we need this just to validate is String is JSON private static final ObjectMapper mapper = tools.jackson.databind.json.JsonMapper.builder() - .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS).build(); - + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .build(); @SuppressWarnings("unchecked") public T fromJson(Object json, Type type) { @@ -56,8 +55,7 @@ public T fromJson(Object json, Type type) { } Collection inputs = (Collection) json; Type itemType = FunctionTypeUtils.getImmediateGenericType(type, 0); - Collection results = FunctionTypeUtils.getRawType(type).isAssignableFrom(List.class) - ? new ArrayList<>() + Collection results = FunctionTypeUtils.getRawType(type).isAssignableFrom(List.class) ? new ArrayList<>() : new HashSet<>(); for (Object input : inputs) { results.add(this.doFromJson(input, itemType)); @@ -104,9 +102,10 @@ else if (value instanceof byte[]) { /** * Performs a simple validation on an object to see if it appears to be a JSON string. - * NOTE: the validation is very rudimentary and simply checks that the object is a String and begins - * and ends with matching pairs of "{}" or "[]" or "\"\"" and therefore may not handle some corner cases. - * Primarily intended for internal of the framework. + * NOTE: the validation is very rudimentary and simply checks that the object is a + * String and begins and ends with matching pairs of "{}" or "[]" or "\"\"" and + * therefore may not handle some corner cases. Primarily intended for internal of the + * framework. * @param value candidate object to evaluate * @return true if and object appears to be a valid JSON string, otherwise false. */ @@ -168,4 +167,5 @@ public static boolean isJsonStringRepresentsMap(Object value) { } return false; } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/DefaultFunctionObservationConvention.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/DefaultFunctionObservationConvention.java index 09eef41f7..82a7805ce 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/DefaultFunctionObservationConvention.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/DefaultFunctionObservationConvention.java @@ -33,7 +33,8 @@ public class DefaultFunctionObservationConvention implements FunctionObservation @Override public KeyValues getLowCardinalityKeyValues(FunctionContext context) { - return KeyValues.of(FunctionObservation.FunctionLowCardinalityTags.FUNCTION_NAME.withValue(context.getTargetFunction().getFunctionDefinition())); + return KeyValues.of(FunctionObservation.FunctionLowCardinalityTags.FUNCTION_NAME + .withValue(context.getTargetFunction().getFunctionDefinition())); } @Override @@ -45,4 +46,5 @@ public String getName() { public String getContextualName(FunctionContext context) { return context.getTargetFunction().getFunctionDefinition() + " process"; } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionContext.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionContext.java index ef22abf2c..42d3e22be 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionContext.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionContext.java @@ -46,4 +46,5 @@ public SimpleFunctionRegistry.FunctionInvocationWrapper getTargetFunction() { public Message getMessage() { return message; } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservation.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservation.java index 57fda6e2b..6fe256def 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservation.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservation.java @@ -60,4 +60,5 @@ public String asString() { } } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservationConvention.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservationConvention.java index 86b8c6891..55da920cd 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservationConvention.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservationConvention.java @@ -32,4 +32,5 @@ public interface FunctionObservationConvention extends ObservationConvention functionObservationConvention) { - return new ObservationFunctionAroundWrapper(registry, - functionObservationConvention.getIfAvailable(() -> null)); + ObjectProvider functionObservationConvention) { + return new ObservationFunctionAroundWrapper(registry, functionObservationConvention.getIfAvailable(() -> null)); } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationFunctionAroundWrapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationFunctionAroundWrapper.java index b1ba9e88c..6500cd3d8 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationFunctionAroundWrapper.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationFunctionAroundWrapper.java @@ -24,18 +24,19 @@ import org.springframework.lang.Nullable; import org.springframework.messaging.Message; - /** * @author Marcin Grzejszczak * @author Oleg Zhurakousky * @since 4.0.0 */ public class ObservationFunctionAroundWrapper extends FunctionAroundWrapper { + private final ObservationRegistry observationRegistry; private final FunctionObservationConvention functionObservationConvention; - public ObservationFunctionAroundWrapper(ObservationRegistry observationRegistry, @Nullable FunctionObservationConvention functionObservationConvention) { + public ObservationFunctionAroundWrapper(ObservationRegistry observationRegistry, + @Nullable FunctionObservationConvention functionObservationConvention) { this.observationRegistry = observationRegistry; this.functionObservationConvention = functionObservationConvention; } @@ -46,11 +47,15 @@ protected Object doApply(Object message, SimpleFunctionRegistry.FunctionInvocati } private Object nonReactorStream(Message message, - SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction) { + SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction) { return functionProcessingObservation(targetFunction, message).observe(() -> targetFunction.apply(message)); } - private Observation functionProcessingObservation(SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction, Message message) { - return FunctionObservation.FUNCTION_PROCESSING_OBSERVATION.observation(this.functionObservationConvention, DefaultFunctionObservationConvention.INSTANCE, () -> new FunctionContext(targetFunction, message), this.observationRegistry); + private Observation functionProcessingObservation(SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction, + Message message) { + return FunctionObservation.FUNCTION_PROCESSING_OBSERVATION.observation(this.functionObservationConvention, + DefaultFunctionObservationConvention.INSTANCE, () -> new FunctionContext(targetFunction, message), + this.observationRegistry); } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java index 023129999..8fda8a4b2 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java @@ -36,8 +36,8 @@ import org.springframework.util.StringUtils; /** - * General utility class which aggregates various class-level utility functions - * used by the framework. + * General utility class which aggregates various class-level utility functions used by + * the framework. * * @author Oleg Zhurakousky * @since 3.0.1 @@ -51,11 +51,10 @@ private FunctionClassUtils() { } /** - * Discovers the start class in the currently running application. - * The discover search order is 'MAIN_CLASS' environment property, - * 'MAIN_CLASS' system property, META-INF/MANIFEST.MF:'Start-Class' attribute, - * meta-inf/manifest.mf:'Start-Class' attribute. - * + * Discovers the start class in the currently running application. The discover search + * order is 'MAIN_CLASS' environment property, 'MAIN_CLASS' system property, + * META-INF/MANIFEST.MF:'Start-Class' attribute, meta-inf/manifest.mf:'Start-Class' + * attribute. * @return instance of Class which represent the start class of the application. */ public static Class getStartClass() { @@ -73,11 +72,11 @@ else if (System.getProperty("MAIN_CLASS") != null) { } else { try { - Class result = getStartClass( - Collections.list(classLoader.getResources(JarFile.MANIFEST_NAME)), classLoader); + Class result = getStartClass(Collections.list(classLoader.getResources(JarFile.MANIFEST_NAME)), + classLoader); if (result == null) { - result = getStartClass(Collections - .list(classLoader.getResources("meta-inf/manifest.mf")), classLoader); + result = getStartClass(Collections.list(classLoader.getResources("meta-inf/manifest.mf")), + classLoader); } Assert.notNull(result, "Failed to locate main class"); mainClass = result; @@ -114,12 +113,15 @@ private static Class getStartClass(List list, ClassLoader classLoader) { Class startClass = ClassUtils.forName(startClassName, classLoader); if (KotlinDetector.isKotlinType(startClass)) { - PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver(classLoader); + PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver( + classLoader); String packageName = startClass.getPackage().getName(); - Resource[] resources = r.getResources("classpath:" + packageName.replace(".", "/") + "/*.class"); + Resource[] resources = r + .getResources("classpath:" + packageName.replace(".", "/") + "/*.class"); for (int i = 0; i < resources.length; i++) { Resource resource = resources[i]; - String className = packageName + "." + (resource.getFilename().replace("/", ".")).replace(".class", ""); + String className = packageName + "." + + (resource.getFilename().replace("/", ".")).replace(".class", ""); startClass = ClassUtils.forName(className, classLoader); if (isSpringBootApplication(startClass)) { logger.info("Loaded Main Kotlin Class: " + startClass); @@ -150,4 +152,5 @@ private static boolean isSpringBootApplication(Class startClass) { return startClass.getDeclaredAnnotation(SpringBootApplication.class) != null || startClass.getDeclaredAnnotation(SpringBootConfiguration.class) != null; } + } 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 2a139a59d..ecc399d9e 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 @@ -14,7 +14,6 @@ * limitations under the License. */ - package org.springframework.cloud.function.utils; import java.util.Locale; @@ -57,11 +56,14 @@ else if (key.startsWith("aws_")) { else if (key.startsWith("solace_")) { return "solace"; } - else if (key.toLowerCase(Locale.ROOT).equals("user-agent") || key.toLowerCase(Locale.ROOT).equals("accept-encoding") || key.toLowerCase(Locale.ROOT).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 } return "origin"; } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/JsonMasker.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/JsonMasker.java index 5fc54fc77..e7338c6eb 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/JsonMasker.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/JsonMasker.java @@ -37,8 +37,6 @@ import org.springframework.cloud.function.json.JsonMapper; import org.springframework.util.ClassUtils; - - /** * @author Oleg Zhurakousky * @author Omer Celik @@ -158,4 +156,5 @@ private static Set loadKeys() { private void addKeys(Set keys) { this.keysToMask.addAll(keys); } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/KotlinUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/KotlinUtils.java index 3e853f770..de9f2b7a5 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/KotlinUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/KotlinUtils.java @@ -25,6 +25,7 @@ * @author Oleg Zhurakousky */ public final class KotlinUtils { + private KotlinUtils() { } @@ -36,4 +37,5 @@ public static boolean isKotlinType(Object object) { } return false; } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/PrimitiveTypesFromStringMessageConverter.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/PrimitiveTypesFromStringMessageConverter.java index ea1a2df1a..516590595 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/PrimitiveTypesFromStringMessageConverter.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/PrimitiveTypesFromStringMessageConverter.java @@ -26,13 +26,11 @@ import org.springframework.util.MimeType; /** - * * @author Oleg Zhurakousky * @since 3.1 */ public class PrimitiveTypesFromStringMessageConverter extends AbstractMessageConverter { - private final ConversionService conversionService; public PrimitiveTypesFromStringMessageConverter(ConversionService conversionService) { @@ -40,7 +38,6 @@ public PrimitiveTypesFromStringMessageConverter(ConversionService conversionServ this.conversionService = conversionService; } - @Override protected boolean supports(Class clazz) { return (Integer.class == clazz || Long.class == clazz); @@ -53,7 +50,9 @@ protected Object convertFromInternal(Message message, Class targetClass, @ @Override @Nullable - protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers, @Nullable Object conversionHint) { + protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers, + @Nullable Object conversionHint) { return payload.toString().getBytes(StandardCharsets.UTF_8); } + } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/SocketUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/SocketUtils.java index 214e4d423..a13843009 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/SocketUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/SocketUtils.java @@ -35,4 +35,5 @@ public static int findAvailableTcpPort() { } return 0; } + } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/actuator/FunctionsEndpointTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/actuator/FunctionsEndpointTests.java index 55015bd46..bc271d263 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/actuator/FunctionsEndpointTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/actuator/FunctionsEndpointTests.java @@ -32,7 +32,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky */ @@ -40,9 +39,9 @@ public class FunctionsEndpointTests { @Test public void ensureIneligibleFunctionWontCauseNPE() { - ApplicationContext context = new SpringApplicationBuilder(SampleConfiguration.class) - .run("--spring.cloud.function.ineligible-definitions=echo,uppercase", - "--spring.main.lazy-initialization=true"); + ApplicationContext context = new SpringApplicationBuilder(SampleConfiguration.class).run( + "--spring.cloud.function.ineligible-definitions=echo,uppercase", + "--spring.main.lazy-initialization=true"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); FunctionsEndpoint endpoint = new FunctionsEndpoint(catalog); Map> allFunctionsinCatalog = endpoint.listAll(); @@ -70,5 +69,7 @@ public Function uppercase() { public Function reverse() { return v -> new StringBuilder(v).reverse().toString(); } + } + } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java index eabc73412..06d04846c 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java @@ -36,12 +36,9 @@ import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.MessageBuilder; - - import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * */ @@ -54,8 +51,7 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProvider() { String id = UUID.randomUUID().toString(); - Message inputMessage = CloudEventMessageBuilder - .withData("{\"name\":\"Ricky\"}") + Message inputMessage = CloudEventMessageBuilder.withData("{\"name\":\"Ricky\"}") .setId(id) .setSource("https://spring.io/") .setType("org.springframework.cloud.function.cloudevent.CloudEventFunctionTests$Person") @@ -65,7 +61,6 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProvider() { Message resultMessage = (Message) function.apply(inputMessage); - /* * Validates that although user only deals with POJO, the framework recognizes * both on input and output that it is dealing with Cloud Event and generates @@ -77,9 +72,10 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProvider() { } /* - * Aside from the properly processing and recognizing CE, the following tow tests (imperative and reactive) - * also emulate message coming from one protocol going to another via MessageUtils.TARGET_PROTOCOL header that - * is set here explicitly but for instance in s-c-stream is set by the framework + * Aside from the properly processing and recognizing CE, the following tow tests + * (imperative and reactive) also emulate message coming from one protocol going to + * another via MessageUtils.TARGET_PROTOCOL header that is set here explicitly but for + * instance in s-c-stream is set by the framework */ @Test public void testBinaryPojoToPojoDefaultOutputHeaderProviderImperative() { @@ -87,14 +83,11 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderImperative() { String id = UUID.randomUUID().toString(); - String payload = "{\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }"; + String payload = "{\n" + " \"version\" : \"1.0\",\n" + + " \"releaseName\" : \"Spring Framework\",\n" + " \"releaseDate\" : \"24-03-2004\"\n" + + " }"; - Message inputMessage = CloudEventMessageBuilder - .withData(payload) + Message inputMessage = CloudEventMessageBuilder.withData(payload) .setId(id) .setSource("https://spring.io/") .setType("org.springframework") @@ -124,14 +117,11 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderReactive() { String id = UUID.randomUUID().toString(); - String payload = "{\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }"; + String payload = "{\n" + " \"version\" : \"1.0\",\n" + + " \"releaseName\" : \"Spring Framework\",\n" + " \"releaseDate\" : \"24-03-2004\"\n" + + " }"; - Message inputMessage = CloudEventMessageBuilder - .withData(payload) + Message inputMessage = CloudEventMessageBuilder.withData(payload) .setId(id) .setSource("https://spring.io/") .setType("org.springframework") @@ -161,14 +151,11 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderReactiveMono() { String id = UUID.randomUUID().toString(); - String payload = "{\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }"; + String payload = "{\n" + " \"version\" : \"1.0\",\n" + + " \"releaseName\" : \"Spring Framework\",\n" + " \"releaseDate\" : \"24-03-2004\"\n" + + " }"; - Message inputMessage = CloudEventMessageBuilder - .withData(payload) + Message inputMessage = CloudEventMessageBuilder.withData(payload) .setId(id) .setSource("https://spring.io/") .setType("org.springframework") @@ -191,7 +178,6 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderReactiveMono() { assertThat(message.getHeaders().get("ce_source")).isEqualTo(URI.create("http://spring.io/")); } - // this kind of emulates that message came from Kafka @SuppressWarnings("unchecked") @Test @@ -200,8 +186,7 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderWithPrefix() { String id = UUID.randomUUID().toString(); - Message inputMessage = CloudEventMessageBuilder - .withData("{\"name\":\"Ricky\"}") + Message inputMessage = CloudEventMessageBuilder.withData("{\"name\":\"Ricky\"}") .setHeader("ce_id", id) .setHeader("ce_source", "https://spring.io/") .setHeader("ce_type", "org.springframework.cloud.function.cloudevent.CloudEventFunctionTests$Person") @@ -218,42 +203,35 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderWithPrefix() { assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(Person.class.getName()); assertThat(CloudEventMessageUtils.getSource(resultMessage)).isEqualTo(URI.create("https://spring.io/")); - assertThat(CloudEventMessageUtils.getTime(resultMessage)).isEqualTo(OffsetDateTime.parse("2024-03-22T03:56:24Z")); + assertThat(CloudEventMessageUtils.getTime(resultMessage)) + .isEqualTo(OffsetDateTime.parse("2024-03-22T03:56:24Z")); } @SuppressWarnings("unchecked") @Test public void testStructuredPojoToPojoDefaultOutputAttributeProvider() throws Exception { - String payload = "{\n" + - " \"specversion\" : \"1.0\",\n" + - " \"type\" : \"org.springframework\",\n" + - " \"source\" : \"https://spring.io/\",\n" + - " \"id\" : \"A234-1234-1234\",\n" + - " \"datacontenttype\" : \"application/json\",\n" + - " \"data\" : {\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }\n" + - "}"; + String payload = "{\n" + " \"specversion\" : \"1.0\",\n" + " \"type\" : \"org.springframework\",\n" + + " \"source\" : \"https://spring.io/\",\n" + " \"id\" : \"A234-1234-1234\",\n" + + " \"datacontenttype\" : \"application/json\",\n" + " \"data\" : {\n" + + " \"version\" : \"1.0\",\n" + " \"releaseName\" : \"Spring Framework\",\n" + + " \"releaseDate\" : \"24-03-2004\"\n" + " }\n" + "}"; Function function = this.lookup("springRelease", TestConfiguration.class); - Message inputMessage = MessageBuilder - .withPayload(payload) - .setHeader(MessageHeaders.CONTENT_TYPE, CloudEventMessageUtils.APPLICATION_CLOUDEVENTS_VALUE + "+json") - .build(); + Message inputMessage = MessageBuilder.withPayload(payload) + .setHeader(MessageHeaders.CONTENT_TYPE, CloudEventMessageUtils.APPLICATION_CLOUDEVENTS_VALUE + "+json") + .build(); assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isFalse(); Message resultMessage = (Message) function.apply(inputMessage); assertThat(resultMessage.getPayload().getReleaseDate()) - .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); + .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); assertThat(resultMessage.getPayload().getVersion()).isEqualTo("2.0"); -// /* -// * Validates that although user only deals with POJO, the framework recognizes -// * both on input and output that it is dealing with Cloud Event and generates -// * appropriate headers/attributes -// */ + // /* + // * Validates that although user only deals with POJO, the framework recognizes + // * both on input and output that it is dealing with Cloud Event and generates + // * appropriate headers/attributes + // */ assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(SpringReleaseEvent.class.getName()); assertThat(CloudEventMessageUtils.getSource(resultMessage)).isEqualTo(URI.create("http://spring.io/")); @@ -262,66 +240,52 @@ public void testStructuredPojoToPojoDefaultOutputAttributeProvider() throws Exce @SuppressWarnings("unchecked") @Test public void testStructuredPojoToPojoMessageFunction() throws Exception { - String payload = "{\n" + - " \"specversion\" : \"1.0\",\n" + - " \"type\" : \"org.springframework\",\n" + - " \"source\" : \"https://spring.io/\",\n" + - " \"id\" : \"A234-1234-1234\",\n" + - " \"datacontenttype\" : \"application/json\",\n" + - " \"data\" : {\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }\n" + - "}"; + String payload = "{\n" + " \"specversion\" : \"1.0\",\n" + " \"type\" : \"org.springframework\",\n" + + " \"source\" : \"https://spring.io/\",\n" + " \"id\" : \"A234-1234-1234\",\n" + + " \"datacontenttype\" : \"application/json\",\n" + " \"data\" : {\n" + + " \"version\" : \"1.0\",\n" + " \"releaseName\" : \"Spring Framework\",\n" + + " \"releaseDate\" : \"24-03-2004\"\n" + " }\n" + "}"; Function function = this.lookup("springReleaseAsMessage", TestConfiguration.class); - Message inputMessage = MessageBuilder - .withPayload(payload) - .setHeader(MessageHeaders.CONTENT_TYPE, CloudEventMessageUtils.APPLICATION_CLOUDEVENTS_VALUE + "+json") - .build(); + Message inputMessage = MessageBuilder.withPayload(payload) + .setHeader(MessageHeaders.CONTENT_TYPE, CloudEventMessageUtils.APPLICATION_CLOUDEVENTS_VALUE + "+json") + .build(); assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isFalse(); Message resultMessage = (Message) function.apply(inputMessage); assertThat(resultMessage.getPayload().getReleaseDate()) - .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); + .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); assertThat(resultMessage.getPayload().getVersion()).isEqualTo("2.0"); -// /* -// * Validates that although user only deals with POJO, the framework recognizes -// * both on input and output that it is dealing with Cloud Event and generates -// * appropriate headers/attributes -// */ + // /* + // * Validates that although user only deals with POJO, the framework recognizes + // * both on input and output that it is dealing with Cloud Event and generates + // * appropriate headers/attributes + // */ assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(SpringReleaseEvent.class.getName()); - assertThat(CloudEventMessageUtils.getSource(resultMessage)).isEqualTo(URI.create("https://spring.release.event")); + assertThat(CloudEventMessageUtils.getSource(resultMessage)) + .isEqualTo(URI.create("https://spring.release.event")); } @SuppressWarnings("unchecked") @Test public void testStructuredPojoToPojoDefaultOutputAttributeProviderNoDataContentType() throws Exception { - String payload = "{\n" + - " \"ce_specversion\" : \"1.0\",\n" + - " \"ce_type\" : \"org.springframework\",\n" + - " \"ce_source\" : \"https://spring.io/\",\n" + - " \"ce_id\" : \"A234-1234-1234\",\n" + - " \"ce_data\" : {\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }\n" + - "}"; + String payload = "{\n" + " \"ce_specversion\" : \"1.0\",\n" + " \"ce_type\" : \"org.springframework\",\n" + + " \"ce_source\" : \"https://spring.io/\",\n" + " \"ce_id\" : \"A234-1234-1234\",\n" + + " \"ce_data\" : {\n" + " \"version\" : \"1.0\",\n" + + " \"releaseName\" : \"Spring Framework\",\n" + " \"releaseDate\" : \"24-03-2004\"\n" + + " }\n" + "}"; Function function = this.lookup("springRelease", TestConfiguration.class); - Message inputMessage = MessageBuilder - .withPayload(payload) - .setHeader(MessageHeaders.CONTENT_TYPE, CloudEventMessageUtils.APPLICATION_CLOUDEVENTS_VALUE + "+json") - .build(); + Message inputMessage = MessageBuilder.withPayload(payload) + .setHeader(MessageHeaders.CONTENT_TYPE, CloudEventMessageUtils.APPLICATION_CLOUDEVENTS_VALUE + "+json") + .build(); assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isFalse(); Message resultMessage = (Message) function.apply(inputMessage); assertThat(resultMessage.getPayload().getReleaseDate()) - .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); + .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); assertThat(resultMessage.getPayload().getVersion()).isEqualTo("2.0"); /* * Validates that although user only deals with POJO, the framework recognizes @@ -334,14 +298,15 @@ public void testStructuredPojoToPojoDefaultOutputAttributeProviderNoDataContentT } private Function lookup(String functionDefinition, Class... configClass) { - ApplicationContext context = new SpringApplicationBuilder(configClass).run( - "--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); + ApplicationContext context = new SpringApplicationBuilder(configClass) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); return context.getBean(FunctionCatalog.class).lookup(functionDefinition); } @EnableAutoConfiguration @Configuration public static class TestConfiguration { + @Bean Function, Message> echo() { return Function.identity(); @@ -395,15 +360,17 @@ Function, Message> springRelease SpringReleaseEvent updated = springRelease().apply(message.getPayload()); assertThat(message.getHeaders().get("ce-type")).isEqualTo("org.springframework"); return CloudEventMessageBuilder.withData(updated) - .copyHeaders(message.getHeaders()) - .setSource("https://spring.release.event") - .setType(SpringReleaseEvent.class.getName()) - .build(); + .copyHeaders(message.getHeaders()) + .setSource("https://spring.release.event") + .setType(SpringReleaseEvent.class.getName()) + .build(); }; } + } public static class Person { + private String name; public String getName() { @@ -413,5 +380,7 @@ public String getName() { public void setName(String name) { this.name = name; } + } + } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java index 3be66e4a7..447e76ce8 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java @@ -31,20 +31,20 @@ */ public class CloudEventMessageUtilsAndBuilderTests { - @Test// see https://github.com/spring-cloud/spring-cloud-function/issues/805 + @Test // see https://github.com/spring-cloud/spring-cloud-function/issues/805 public void testHeaderKeyInsensitivity() { Message httpMessage = MessageBuilder.withPayload("hello") - .setHeader("cE-SoUrCe", "https://foo.bar") - .setHeader("Ce-specVeRsion", "1.0") - .setHeader("Ce-Type", "blah") - .setHeader("x", "x") - .setHeader("zzz", "zzz") - .build(); + .setHeader("cE-SoUrCe", "https://foo.bar") + .setHeader("Ce-specVeRsion", "1.0") + .setHeader("Ce-Type", "blah") + .setHeader("x", "x") + .setHeader("zzz", "zzz") + .build(); assertThat(CloudEventMessageUtils.isCloudEvent(httpMessage)).isTrue(); } - @Test// see https://github.com/spring-cloud/spring-cloud-function/issues/680 + @Test // see https://github.com/spring-cloud/spring-cloud-function/issues/680 public void testProperAttributeExtractionRegardlessOfTargetProtocol() { Message ceMessage = CloudEventMessageBuilder.withData("foo").build(); ceMessage = MessageBuilder.fromMessage(ceMessage).setHeader("kafka_foo", "blah").build(); @@ -67,19 +67,22 @@ public void testProperAttributeExtractionRegardlessOfTargetProtocol() { @Test public void testAttributeRecognitionAndCanonicalization() { Message httpMessage = MessageBuilder.withPayload("hello") - .setHeader(CloudEventMessageUtils.SOURCE, "https://foo.bar") - .setHeader(CloudEventMessageUtils.SPECVERSION, "1.0") - .setHeader(CloudEventMessageUtils.TYPE, "blah") - .setHeader("x", "x") - .setHeader("zzz", "zzz") - .build(); + .setHeader(CloudEventMessageUtils.SOURCE, "https://foo.bar") + .setHeader(CloudEventMessageUtils.SPECVERSION, "1.0") + .setHeader(CloudEventMessageUtils.TYPE, "blah") + .setHeader("x", "x") + .setHeader("zzz", "zzz") + .build(); Map attributes = CloudEventMessageUtils.getAttributes(httpMessage); assertThat(attributes.size()).isEqualTo(3); assertThat((String) CloudEventMessageUtils.getData(httpMessage)).isEqualTo("hello"); - Message kafkaMessage = CloudEventMessageBuilder.fromMessage(httpMessage).build(CloudEventMessageUtils.KAFKA_ATTR_PREFIX); + Message kafkaMessage = CloudEventMessageBuilder.fromMessage(httpMessage) + .build(CloudEventMessageUtils.KAFKA_ATTR_PREFIX); attributes = CloudEventMessageUtils.getAttributes(kafkaMessage); - assertThat(attributes.size()).isEqualTo(4); // id will be auto injected, so always at least 4 (as tehre are 4 required attributes in CE) + assertThat(attributes.size()).isEqualTo(4); // id will be auto injected, so always + // at least 4 (as tehre are 4 required + // attributes in CE) assertThat(kafkaMessage.getHeaders().get("ce_source")).isNotNull(); assertThat(CloudEventMessageUtils.getSource(kafkaMessage)).isEqualTo(URI.create("https://foo.bar")); assertThat(kafkaMessage.getHeaders().get("ce_type")).isNotNull(); @@ -87,7 +90,8 @@ public void testAttributeRecognitionAndCanonicalization() { assertThat(kafkaMessage.getHeaders().get("ce_specversion")).isNotNull(); assertThat(CloudEventMessageUtils.getSpecVersion(kafkaMessage)).isEqualTo("1.0"); - httpMessage = CloudEventMessageBuilder.fromMessage(kafkaMessage).build(CloudEventMessageUtils.DEFAULT_ATTR_PREFIX); + httpMessage = CloudEventMessageBuilder.fromMessage(kafkaMessage) + .build(CloudEventMessageUtils.DEFAULT_ATTR_PREFIX); attributes = CloudEventMessageUtils.getAttributes(httpMessage); assertThat(attributes.size()).isEqualTo(4); // assertThat(httpMessage.getHeaders().get("ce-source")).isNotNull(); @@ -108,34 +112,33 @@ void canonicalizeHeadersWithPossibleCopyReturnsCopyWithUpdatedHeadersWhenModifie // amqpAttrs -> modified // structured -> modified Message inputMessage = MessageBuilder.withPayload("hello") - .setHeader("ce_foo", "bar") - .setHeader("x", "x1") - .setHeader("x|x", "x2") - .build(); + .setHeader("ce_foo", "bar") + .setHeader("x", "x1") + .setHeader("x|x", "x2") + .build(); Message updatedMessage = CloudEventMessageUtils.canonicalizeHeadersWithPossibleCopy(inputMessage); assertThat(inputMessage).isNotSameAs(updatedMessage); - assertThat(updatedMessage.getHeaders()) - .containsEntry("ce-foo", "bar") - .containsEntry("x", "x1") - .containsEntry("x|x", "x2"); + assertThat(updatedMessage.getHeaders()).containsEntry("ce-foo", "bar") + .containsEntry("x", "x1") + .containsEntry("x|x", "x2"); } @Test void canonicalizeHeadersWithPossibleCopyReturnsSameInstanceWhenNotModified() { Message inputMessage = MessageBuilder.withPayload("hello") - .setHeader("ce-foo", "bar") - .setHeader("x", "x1") - .setHeader("x|x", "x2") - .build(); + .setHeader("ce-foo", "bar") + .setHeader("x", "x1") + .setHeader("x|x", "x2") + .build(); Message updatedMessage = CloudEventMessageUtils.canonicalizeHeadersWithPossibleCopy(inputMessage); assertThat(inputMessage).isSameAs(updatedMessage); - assertThat(updatedMessage.getHeaders()) - .containsEntry("ce-foo", "bar") - .containsEntry("x", "x1") - .containsEntry("x|x", "x2"); + assertThat(updatedMessage.getHeaders()).containsEntry("ce-foo", "bar") + .containsEntry("x", "x1") + .containsEntry("x|x", "x2"); } + } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/SpringReleaseEvent.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/SpringReleaseEvent.java index 64fd75d3c..784cb3a77 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/SpringReleaseEvent.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/SpringReleaseEvent.java @@ -62,6 +62,8 @@ public void setVersion(String version) { @Override public String toString() { - return "releaseDate:" + new SimpleDateFormat("dd-MM-yyyy").format(releaseDate) + "; releaseName:" + releaseName + "; version:" + version; + return "releaseDate:" + new SimpleDateFormat("dd-MM-yyyy").format(releaseDate) + "; releaseName:" + releaseName + + "; version:" + version; } + } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HeaderMappingTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HeaderMappingTests.java index 1d9b0671b..b14cafb47 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HeaderMappingTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HeaderMappingTests.java @@ -38,13 +38,12 @@ public class HeaderMappingTests { @Test public void testErrorWarnAndContinue() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echoFail.input-header-mapping-expression[0].key1=hello1", - "--spring.cloud.function.configuration.echoFail.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echoFail.input-header-mapping-expression[0].foo=headers.contentType")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.echoFail.input-header-mapping-expression[0].key1=hello1", + "--spring.cloud.function.configuration.echoFail.input-header-mapping-expression[0].key2='hello2'", + "--spring.cloud.function.configuration.echoFail.input-header-mapping-expression[0].foo=headers.contentType")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("echoFail"); @@ -54,139 +53,140 @@ public void testErrorWarnAndContinue() throws Exception { @Test public void testInputHeaderMappingPropertyWithIndex() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key1='hello1'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].foo=headers.contentType")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key1='hello1'", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key2='hello2'", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].foo=headers.contentType")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("echo"); function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build()); } } @Test public void testInputHeaderMappingPropertyWithIndexMix() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key1='hello1'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.foo=headers.contentType")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key1='hello1'", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key2='hello2'", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression.foo=headers.contentType")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("echo"); function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build()); } } @Test public void testInputHeaderMappingPropertyWithIndexMixDeux() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.key1='hello1'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.foo=headers.contentType")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression.key1='hello1'", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key2='hello2'", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression.foo=headers.contentType")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("echo"); function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build()); } } @Test public void testInputHeaderMappingPropertyWithoutIndex() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.key1='hello1'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.key2='hello2'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.foo=headers.contentType")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression.key1='hello1'", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression.key2='hello2'", + "--spring.cloud.function.configuration.echo.input-header-mapping-expression.foo=headers.contentType")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("echo"); function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build()); } } @Test public void testInputHeaderMappingExpressionWithCompositionWithIndex() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression[0].key1='hello1'", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression[0].foo=headers.contentType")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression[0].key1='hello1'", + "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression[0].key2='hello2'", + "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression[0].foo=headers.contentType")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("echo|foo"); function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build()); } } @Test public void testInputHeaderMappingExpressionWithCompositionWithoutIndex() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression.key1='hello1'", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression.key2='hello2'", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression.foo=headers.contentType")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression.key1='hello1'", + "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression.key2='hello2'", + "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression.foo=headers.contentType")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("echo|foo"); function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build()); - //assertions are in 'echo' function since we're validating what's coming into it. + // assertions are in 'echo' function since we're validating what's coming into + // it. } } @Test public void testInputHeaderMappingPropertyWithSplitExpression() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key1=headers.path.split('/')[0]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key2=headers.path.split('/')[1]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key3=headers.path")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.split.input-header-mapping-expression.key1=headers.path.split('/')[0]", + "--spring.cloud.function.configuration.split.input-header-mapping-expression.key2=headers.path.split('/')[1]", + "--spring.cloud.function.configuration.split.input-header-mapping-expression.key3=headers.path")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("split"); function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") - .setHeader("path", "foo/bar/baz").build()); + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .setHeader("path", "foo/bar/baz") + .build()); } } @SuppressWarnings("unchecked") @Test public void testOutputHeaderMapping() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.foo.output-header-mapping-expression.keyOut1='hello1'", - "--spring.cloud.function.configuration.foo.output-header-mapping-expression.keyOut2=headers.contentType")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.foo.output-header-mapping-expression.keyOut1='hello1'", + "--spring.cloud.function.configuration.foo.output-header-mapping-expression.keyOut2=headers.contentType")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("foo"); Message result = (Message) function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build()); assertThat(result.getHeaders().containsKey("keyOut1")).isTrue(); assertThat(result.getHeaders().get("keyOut1")).isEqualTo("hello1"); assertThat(result.getHeaders().containsKey("keyOut2")).isTrue(); @@ -197,21 +197,21 @@ public void testOutputHeaderMapping() throws Exception { @SuppressWarnings("unchecked") @Test public void testMixedInputOutputHeaderMapping() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut1='hello1'", - "--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut2=headers.contentType", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key1=headers.path.split('/')[0]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key2=headers.path.split('/')[1]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key3=headers.path")) { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) + .web(WebApplicationType.NONE) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut1='hello1'", + "--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut2=headers.contentType", + "--spring.cloud.function.configuration.split.input-header-mapping-expression.key1=headers.path.split('/')[0]", + "--spring.cloud.function.configuration.split.input-header-mapping-expression.key2=headers.path.split('/')[1]", + "--spring.cloud.function.configuration.split.input-header-mapping-expression.key3=headers.path")) { FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); FunctionInvocationWrapper function = functionCatalog.lookup("split"); Message result = (Message) function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") - .setHeader("path", "foo/bar/baz").build()); + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .setHeader("path", "foo/bar/baz") + .build()); assertThat(result.getHeaders().containsKey("keyOut1")).isTrue(); assertThat(result.getHeaders().get("keyOut1")).isEqualTo("hello1"); assertThat(result.getHeaders().containsKey("keyOut2")).isTrue(); @@ -260,5 +260,7 @@ public Function, Message> foo() { return x; }; } + } + } 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 6b219cae9..75cb8a4a2 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 @@ -33,7 +33,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * */ @@ -43,8 +42,8 @@ public class HybridFunctionalRegistrationTests { @SuppressWarnings("rawtypes") @Test public void testNoDoubleRegistrationInHybridMode() { - ConfigurableApplicationContext context = FunctionalSpringApplication - .run(UppercaseFunction.class, "--spring.functional.enabled=false"); + ConfigurableApplicationContext context = FunctionalSpringApplication.run(UppercaseFunction.class, + "--spring.functional.enabled=false"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); @@ -56,8 +55,8 @@ public void testNoDoubleRegistrationInHybridMode() { @SuppressWarnings("rawtypes") @Test public void testMessageHeaderPropagationInFunctionalBeanRegistration() { - ConfigurableApplicationContext context = FunctionalSpringApplication - .run(UppercaseMessageFunction.class, "--spring.functional.enabled=false"); + ConfigurableApplicationContext context = FunctionalSpringApplication.run(UppercaseMessageFunction.class, + "--spring.functional.enabled=false"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); @@ -65,41 +64,38 @@ public void testMessageHeaderPropagationInFunctionalBeanRegistration() { assertThat(context.getBeansOfType(UppercaseMessageFunction.class).size()).isEqualTo(1); Function f = catalog.lookup(Function.class, "hybridFunctionalRegistrationTests.UppercaseMessageFunction"); assertThat(f).isNotNull(); - String result = (String) f.apply(MessageBuilder.withPayload("hello").setHeader("foo", "foo").setHeader("blah", "blah").build()); + String result = (String) f + .apply(MessageBuilder.withPayload("hello").setHeader("foo", "foo").setHeader("blah", "blah").build()); assertThat(result).isEqualTo("HELLO"); } @SuppressWarnings("rawtypes") @Test public void testNoDoubleRegistrationInHybridModeFluxedFunction() { - ConfigurableApplicationContext context = FunctionalSpringApplication - .run(UppercaseFluxFunction.class, "--spring.functional.enabled=false"); + ConfigurableApplicationContext context = FunctionalSpringApplication.run(UppercaseFluxFunction.class, + "--spring.functional.enabled=false"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); assertThat(context.containsBean("function")).isTrue(); assertThat(context.getBeansOfType(UppercaseFluxFunction.class).size()).isEqualTo(1); - assertThat((Function) catalog.lookup(Function.class, "hybridFunctionalRegistrationTests.UppercaseFluxFunction")).isNotNull(); + assertThat((Function) catalog.lookup(Function.class, "hybridFunctionalRegistrationTests.UppercaseFluxFunction")) + .isNotNull(); } @SpringBootConfiguration(proxyBeanMethods = false) - @ImportAutoConfiguration({ - ContextFunctionCatalogAutoConfiguration.class, - JacksonAutoConfiguration.class } - ) + @ImportAutoConfiguration({ ContextFunctionCatalogAutoConfiguration.class, JacksonAutoConfiguration.class }) public static class UppercaseFunction implements Function { @Override public String apply(String t) { return t.toUpperCase(Locale.ROOT); } + } @SpringBootConfiguration(proxyBeanMethods = false) - @ImportAutoConfiguration({ - ContextFunctionCatalogAutoConfiguration.class, - JacksonAutoConfiguration.class } - ) + @ImportAutoConfiguration({ ContextFunctionCatalogAutoConfiguration.class, JacksonAutoConfiguration.class }) public static class UppercaseMessageFunction implements Function, String> { @Override @@ -108,13 +104,11 @@ public String apply(Message message) { assertThat(message.getHeaders().get("blah")).isEqualTo("blah"); return message.getPayload().toUpperCase(Locale.ROOT); } + } @SpringBootConfiguration(proxyBeanMethods = false) - @ImportAutoConfiguration({ - ContextFunctionCatalogAutoConfiguration.class, - JacksonAutoConfiguration.class } - ) + @ImportAutoConfiguration({ ContextFunctionCatalogAutoConfiguration.class, JacksonAutoConfiguration.class }) public static class UppercaseFluxFunction implements Function, Flux> { @Override @@ -122,6 +116,6 @@ public Flux apply(Flux flux) { return flux.map(v -> v.toUpperCase(Locale.ROOT)); } - } + } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/MessageRoutingCallbackTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/MessageRoutingCallbackTests.java index 2282060ce..d01070fa2 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/MessageRoutingCallbackTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/MessageRoutingCallbackTests.java @@ -50,11 +50,14 @@ public void before() { public void testRoutingCallbackWithMessageModification() { FunctionCatalog catalog = this.configureCatalog(SamppleConfiguration.class); SamppleConfiguration conf = context.getBean(SamppleConfiguration.class); - FunctionInvocationWrapper function = (FunctionInvocationWrapper) catalog.lookup(RoutingFunction.FUNCTION_NAME, "application/json"); + FunctionInvocationWrapper function = (FunctionInvocationWrapper) catalog.lookup(RoutingFunction.FUNCTION_NAME, + "application/json"); String foo = "{\"foo\":\"blah\"}"; - Message fooResult = (Message) function.apply(MessageBuilder.withPayload(foo.getBytes()).build()); + Message fooResult = (Message) function + .apply(MessageBuilder.withPayload(foo.getBytes()).build()); String bar = "{\"bar\":\"blah\"}"; - Message barResult = (Message) function.apply(MessageBuilder.withPayload(bar.getBytes()).build()); + Message barResult = (Message) function + .apply(MessageBuilder.withPayload(bar.getBytes()).build()); assertThat(fooResult.getPayload()).isEqualTo("\"foo\"".getBytes()); assertThat(barResult.getPayload()).isEqualTo("\"bar\"".getBytes()); @@ -64,8 +67,7 @@ public void testRoutingCallbackWithMessageModification() { private FunctionCatalog configureCatalog(Class... configClass) { this.context = new SpringApplicationBuilder(configClass) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); return catalog; } @@ -119,10 +121,11 @@ public Function, Message> bar() { return m; }; } - } + } public static class Foo { + private String foo; public String getFoo() { @@ -132,9 +135,11 @@ public String getFoo() { public void setFoo(String foo) { this.foo = foo; } + } public static class Bar { + private String bar; public String getBar() { @@ -144,5 +149,7 @@ public String getBar() { public void setBar(String bar) { this.bar = bar; } + } + } 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 6f5cf8418..1efaedba4 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 @@ -50,7 +50,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * */ @@ -58,19 +57,20 @@ public class BeanFactoryAwareFunctionRegistryMultiInOutTests { private FunctionCatalog configureCatalog() { ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG"); + .run("--logging.level.org.springframework.cloud.function=DEBUG"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); return catalog; } /* - * This test validates , Flux> without any type conversion + * This test validates , Flux> without any type + * conversion */ @Test public void testMultiInput() { FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple"); + Function, Flux>, Flux> multiInputFunction = catalog + .lookup("multiInputSingleOutputViaReactiveTuple"); Flux stringStream = Flux.just("one", "two", "three"); Flux intStream = Flux.just(1, 2, 3); @@ -83,8 +83,8 @@ public void testMultiInput() { @Test public void testMultiInputWithPojoConversion() { FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("thomas", "application/json"); + Function, Flux>, Flux> multiInputFunction = catalog + .lookup("thomas", "application/json"); CartEvent carEvent = new CartEvent(); carEvent.setCarEvent("carEvent"); Flux carEventStream = Flux.just(carEvent); @@ -104,26 +104,27 @@ public void testMultiInputWithPojoConversion() { @Disabled public void testMultiInputBiFunction() { FunctionCatalog catalog = this.configureCatalog(); - BiFunction, Flux, Flux> multiInputFunction = - catalog.lookup(BiFunction.class, "multiInputSingleOutputViaBiFunction"); + BiFunction, Flux, Flux> multiInputFunction = catalog.lookup(BiFunction.class, + "multiInputSingleOutputViaBiFunction"); Flux stringStream = Flux.just("one", "two", "three"); Flux intStream = Flux.just(1, 2, 3); -// List result = multiInputFunction.apply(Tuples.of(stringStream, intStream)).collectList().block(); -// System.out.println(result); + // List result = multiInputFunction.apply(Tuples.of(stringStream, + // intStream)).collectList().block(); + // System.out.println(result); } /* - * This test invokes the same function as above but with types reversed. - * While the target function remains , Flux> - * it is actually invoked as Tuple2, Flux> - * hence showcasing type conversion using Spring's ConversionService + * This test invokes the same function as above but with types reversed. While the + * target function remains , Flux> it is actually invoked + * as Tuple2, Flux> hence showcasing type conversion using + * Spring's ConversionService */ @Test public void testMultiInputWithConversion() { FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple"); + Function, Flux>, Flux> multiInputFunction = catalog + .lookup("multiInputSingleOutputViaReactiveTuple"); Flux stringStream = Flux.just(11, 22, 33); Flux intStream = Flux.just("1", "2", "2"); @@ -141,8 +142,8 @@ public void testMultiInputWithConversion() { @Disabled public void testMultiInputWithComposition() { FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple|uppercase"); + Function, Flux>, Flux> multiInputFunction = catalog + .lookup("multiInputSingleOutputViaReactiveTuple|uppercase"); Flux stringStream = Flux.just("one", "two", "three"); Flux intStream = Flux.just("1", "2", "3"); @@ -151,14 +152,13 @@ public void testMultiInputWithComposition() { } /* - * This is basically the repeater function currently prototyped in Riff - * The only difference it uses Tuple2 instead of BiFunction (which we will support anyway) + * This is basically the repeater function currently prototyped in Riff The only + * difference it uses Tuple2 instead of BiFunction (which we will support anyway) */ @Test public void testMultiOutputAsArray() { FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux[]> repeater = - catalog.lookup("repeater"); + Function, Flux>, Flux[]> repeater = catalog.lookup("repeater"); Flux stringStream = Flux.just("one", "two", "three"); Flux intStream = Flux.just(3, 2, 1); @@ -167,19 +167,17 @@ public void testMultiOutputAsArray() { result[1].subscribe(System.out::println); } - /* - * This test demonstrates single input into multiple outputs - * as Tuple3 thus making output types known. + * This test demonstrates single input into multiple outputs as Tuple3 thus making + * output types known. * - * The input is a POJO (Person) - * no conversion + * The input is a POJO (Person) no conversion */ @Test public void testMultiOutputAsTuplePojoInInputTypeMatch() { FunctionCatalog catalog = this.configureCatalog(); - Function, Tuple3, Flux, Flux>> multiOutputFunction = - catalog.lookup("multiOutputAsTuplePojoIn"); + Function, Tuple3, Flux, Flux>> multiOutputFunction = catalog + .lookup("multiOutputAsTuplePojoIn"); Flux personStream = Flux.just(new Person("Uncle Sam", 1), new Person("Oncle Pierre", 2)); Tuple3, Flux, Flux> result = multiOutputFunction.apply(personStream); @@ -189,22 +187,24 @@ public void testMultiOutputAsTuplePojoInInputTypeMatch() { } /* - * This test is identical to the previous one with the exception that the - * input is a Message with payload as JSON byte array representation of Person (expected by the target function), - * thus demonstrating Message Conversion + * This test is identical to the previous one with the exception that the input is a + * Message with payload as JSON byte array representation of Person (expected by the + * target function), thus demonstrating Message Conversion */ @Test public void testMultiOutputAsTuplePojoInInputByteArray() { FunctionCatalog catalog = this.configureCatalog(); - Function>, Tuple3, Flux, Flux>> multiOutputFunction = - catalog.lookup("multiOutputAsTuplePojoIn"); - - Message uncleSam = MessageBuilder.withPayload("{\"name\":\"Uncle Sam\",\"id\":1}".getBytes(StandardCharsets.UTF_8)) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(); - Message unclePierre = MessageBuilder.withPayload("{\"name\":\"Oncle Pierre\",\"id\":2}".getBytes(StandardCharsets.UTF_8)) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(); + Function>, Tuple3, Flux, Flux>> multiOutputFunction = catalog + .lookup("multiOutputAsTuplePojoIn"); + + Message uncleSam = MessageBuilder + .withPayload("{\"name\":\"Uncle Sam\",\"id\":1}".getBytes(StandardCharsets.UTF_8)) + .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) + .build(); + Message unclePierre = MessageBuilder + .withPayload("{\"name\":\"Oncle Pierre\",\"id\":2}".getBytes(StandardCharsets.UTF_8)) + .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) + .build(); Flux> personStream = Flux.just(uncleSam, unclePierre); Tuple3, Flux, Flux> result = multiOutputFunction.apply(personStream); @@ -214,23 +214,25 @@ public void testMultiOutputAsTuplePojoInInputByteArray() { } /* - * This is another variation of the above. In this case the signature of the target function is - * >, Tuple3, Flux, Flux>> yet we are sending - * Message with payload as byte[] which is converted to Person and then embedded in new Message - * passed to a function + * This is another variation of the above. In this case the signature of the target + * function is >, Tuple3, Flux, + * Flux>> yet we are sending Message with payload as byte[] which is + * converted to Person and then embedded in new Message passed to a function */ @Test public void testMultiOutputAsTuplePojoInInputByteArrayInputTypePojoMessage() { FunctionCatalog catalog = this.configureCatalog(); - Function>, Tuple3, Flux, Flux>> multiOutputFunction = - catalog.lookup("multiOutputAsTupleMessageIn"); - - Message uncleSam = MessageBuilder.withPayload("{\"name\":\"Uncle Sam\",\"id\":1}".getBytes(StandardCharsets.UTF_8)) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(); - Message unclePierre = MessageBuilder.withPayload("{\"name\":\"Oncle Pierre\",\"id\":2}".getBytes(StandardCharsets.UTF_8)) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(); + Function>, Tuple3, Flux, Flux>> multiOutputFunction = catalog + .lookup("multiOutputAsTupleMessageIn"); + + Message uncleSam = MessageBuilder + .withPayload("{\"name\":\"Uncle Sam\",\"id\":1}".getBytes(StandardCharsets.UTF_8)) + .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) + .build(); + Message unclePierre = MessageBuilder + .withPayload("{\"name\":\"Oncle Pierre\",\"id\":2}".getBytes(StandardCharsets.UTF_8)) + .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) + .build(); Flux> personStream = Flux.just(uncleSam, unclePierre); Tuple3, Flux, Flux> result = multiOutputFunction.apply(personStream); @@ -242,8 +244,8 @@ public void testMultiOutputAsTuplePojoInInputByteArrayInputTypePojoMessage() { @Test public void testMultiToMulti() { FunctionCatalog catalog = this.configureCatalog(); - Function, Flux, Flux>, Tuple2, Mono>> multiToMulti = - catalog.lookup("multiToMulti"); + Function, Flux, Flux>, Tuple2, Mono>> multiToMulti = catalog + .lookup("multiToMulti"); Flux firstFlux = Flux.just("Unlce", "Oncle"); Flux secondFlux = Flux.just("Sam", "Pierre"); @@ -258,15 +260,23 @@ public void testMultiToMulti() { @Disabled public void testMultiToMultiWithMessageByteArrayPayload() { FunctionCatalog catalog = this.configureCatalog(); - Function>, Flux>, Flux>>, Tuple2>, Mono>>> multiTuMulti = - catalog.lookup("multiToMulti", "foo/bar,application/json", "application/json"); + Function>, Flux>, Flux>>, Tuple2>, Mono>>> multiTuMulti = catalog + .lookup("multiToMulti", "foo/bar,application/json", "application/json"); Flux> firstFlux = Flux.just( - MessageBuilder.withPayload("Unlce".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build(), - MessageBuilder.withPayload("Onlce".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build()); + MessageBuilder.withPayload("Unlce".getBytes()) + .setHeader(MessageHeaders.CONTENT_TYPE, "text/plain") + .build(), + MessageBuilder.withPayload("Onlce".getBytes()) + .setHeader(MessageHeaders.CONTENT_TYPE, "text/plain") + .build()); Flux> secondFlux = Flux.just( - MessageBuilder.withPayload("Sam".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build(), - MessageBuilder.withPayload("Pierre".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build()); + MessageBuilder.withPayload("Sam".getBytes()) + .setHeader(MessageHeaders.CONTENT_TYPE, "text/plain") + .build(), + MessageBuilder.withPayload("Pierre".getBytes()) + .setHeader(MessageHeaders.CONTENT_TYPE, "text/plain") + .build()); ByteBuffer one = ByteBuffer.allocate(4); one.putInt(1); @@ -274,10 +284,15 @@ public void testMultiToMultiWithMessageByteArrayPayload() { two.putInt(2); Flux> thirdFlux = Flux.just( - MessageBuilder.withPayload(one.array()).setHeader(MessageHeaders.CONTENT_TYPE, "octet-stream/integer").build(), - MessageBuilder.withPayload(two.array()).setHeader(MessageHeaders.CONTENT_TYPE, "octet-stream/integer").build()); - - Tuple2>, Mono>> result = multiTuMulti.apply(Tuples.of(firstFlux, secondFlux, thirdFlux)); + MessageBuilder.withPayload(one.array()) + .setHeader(MessageHeaders.CONTENT_TYPE, "octet-stream/integer") + .build(), + MessageBuilder.withPayload(two.array()) + .setHeader(MessageHeaders.CONTENT_TYPE, "octet-stream/integer") + .build()); + + Tuple2>, Mono>> result = multiTuMulti + .apply(Tuples.of(firstFlux, secondFlux, thirdFlux)); ObjectMapper mapper = new ObjectMapper(); result.getT1().subscribe(v -> { try { @@ -297,7 +312,6 @@ public void testMultiToMultiWithMessageByteArrayPayload() { }); } - @EnableAutoConfiguration @Configuration protected static class SampleFunctionConfiguration { @@ -354,15 +368,15 @@ public Function, Flux, Flux>, Tuple2 nameFlux = tuple.getT2(); Flux idFlux = tuple.getT3(); Flux person = toStringFlux.zipWith(nameFlux) - .map(t -> t.getT1() + " " + t.getT2()) - .zipWith(idFlux) - .map(t -> new Person(t.getT1(), t.getT2())); + .map(t -> t.getT1() + " " + t.getT2()) + .zipWith(idFlux) + .map(t -> new Person(t.getT1(), t.getT2())); return Tuples.of(person, person.count()); }; } @Bean - public MessageConverter byteArrayToIntegerMessageConverter() { + public MessageConverter byteArrayToIntegerMessageConverter() { return new AbstractMessageConverter(MimeTypeUtils.parseMimeType("octet-stream/integer")) { @Override @@ -371,15 +385,15 @@ protected boolean supports(Class clazz) { } @Override - protected Object convertFromInternal( - Message message, Class targetClass, @Nullable Object conversionHint) { + protected Object convertFromInternal(Message message, Class targetClass, + @Nullable Object conversionHint) { ByteBuffer wrappedPayload = ByteBuffer.wrap((byte[]) message.getPayload()); return wrappedPayload.getInt(); } @Override - protected Object convertToInternal( - Object payload, @Nullable MessageHeaders headers, @Nullable Object conversionHint) { + protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers, + @Nullable Object conversionHint) { return null; } @@ -395,13 +409,10 @@ public Function, Flux>, Flux[]> repeater() { Flux sharedIntFlux = integerFlux.publish().autoConnect(2); - Flux repeated = stringFlux - .zipWith(sharedIntFlux) - .flatMap(t -> Flux.fromIterable(Collections.nCopies(t.getT2(), t.getT1()))); + Flux repeated = stringFlux.zipWith(sharedIntFlux) + .flatMap(t -> Flux.fromIterable(Collections.nCopies(t.getT2(), t.getT1()))); - Flux sum = sharedIntFlux - .buffer(3, 1) - .map(l -> l.stream().mapToInt(Integer::intValue).sum()); + Flux sum = sharedIntFlux.buffer(3, 1).map(l -> l.stream().mapToInt(Integer::intValue).sum()); return new Flux[] { repeated, sum }; }; @@ -420,9 +431,11 @@ public Function, Flux>, Flux> }); }; } + } public static class CartEvent { + private String carEvent; public String getCarEvent() { @@ -432,9 +445,11 @@ public String getCarEvent() { public void setCarEvent(String carEvent) { this.carEvent = carEvent; } + } public static class CheckoutEvent { + private String checkoutEvent; public String getCheckoutEvent() { @@ -444,9 +459,11 @@ public String getCheckoutEvent() { public void setCheckoutEvent(String checkoutEvent) { this.checkoutEvent = checkoutEvent; } + } public static class OrderEvent { + private String orderEvent; public String getOrderEvent() { @@ -456,33 +473,45 @@ public String getOrderEvent() { public void setOrderEvent(String orderEvent) { this.orderEvent = orderEvent; } + } public static class Person { + private String name; + private int id; + public Person() { } + public Person(String name, int id) { this.name = name; this.id = id; } + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public int getId() { return id; } + public void setId(int id) { this.id = id; } + @Override public String toString() { return "Person: " + name + "/" + id; } + } + } 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 d01f51875..a61928edb 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 @@ -16,7 +16,6 @@ package org.springframework.cloud.function.context.catalog; - import java.io.Serializable; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; @@ -82,7 +81,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * @author Artem Bilan * @@ -97,8 +95,7 @@ private FunctionCatalog configureCatalog() { private FunctionCatalog configureCatalog(Class... configClass) { this.context = new SpringApplicationBuilder(configClass) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); return catalog; } @@ -124,7 +121,7 @@ public void testCompositionWithNonExistingFunction() throws Exception { for (int i = 0; i < 10; i++) { catalog.lookup("echo1|any"); } - Field functionRegistrationsField = ReflectionUtils.findField(catalog.getClass(), "functionRegistrations"); + Field functionRegistrationsField = ReflectionUtils.findField(catalog.getClass(), "functionRegistrations"); functionRegistrationsField.setAccessible(true); Set functionRegistrations = (Set) functionRegistrationsField.get(catalog); assertThat(functionRegistrations.size()).isEqualTo(1); @@ -159,14 +156,15 @@ public void testCompositionReactiveSupplierWithImplicitConsumer() throws Excepti CompositionReactiveSupplierWithConsumer.results.clear(); function = catalog.lookup("functionMessage|consume"); - function.apply(Flux.fromArray(new Message[] {MessageBuilder.withPayload("ricky").build(), MessageBuilder.withPayload("bubbles").build()})); + function.apply(Flux.fromArray(new Message[] { MessageBuilder.withPayload("ricky").build(), + MessageBuilder.withPayload("bubbles").build() })); assertThat(CompositionReactiveSupplierWithConsumer.results.size()).isEqualTo(2); assertThat(CompositionReactiveSupplierWithConsumer.results.get(0)).isEqualTo("RICKY"); assertThat(CompositionReactiveSupplierWithConsumer.results.get(1)).isEqualTo("BUBBLES"); CompositionReactiveSupplierWithConsumer.results.clear(); function = catalog.lookup("functionPrimitive|consume"); - function.apply(Flux.fromArray(new String[] {"ricky", "bubbles"})); + function.apply(Flux.fromArray(new String[] { "ricky", "bubbles" })); assertThat(CompositionReactiveSupplierWithConsumer.results.size()).isEqualTo(2); assertThat(CompositionReactiveSupplierWithConsumer.results.get(0)).isEqualTo("RICKY"); assertThat(CompositionReactiveSupplierWithConsumer.results.get(1)).isEqualTo("BUBBLES"); @@ -212,7 +210,7 @@ public void testFunctionEligibilityFiltering() { } } System.out.println(registeredFunction); - //assertThat(registeredFunction.size()).isEqualTo(5); + // assertThat(registeredFunction.size()).isEqualTo(5); assertThat((FunctionInvocationWrapper) catalog.lookup("asJsonNode")).isNull(); } @@ -220,7 +218,9 @@ public void testFunctionEligibilityFiltering() { public void testJsonNodeAsInput() throws Exception { FunctionCatalog catalog = this.configureCatalog(JsonNodeConfiguration.class); Function, Message> f = catalog.lookup("messageAsJsonNode", "application/json"); - Message m = MessageBuilder.withPayload("[{\"name\":\"bob\"}, {\"name\":\"bob\"}]").setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build(); + Message m = MessageBuilder.withPayload("[{\"name\":\"bob\"}, {\"name\":\"bob\"}]") + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build(); assertThat(new String(f.apply(m).getPayload())).isEqualTo("[{\"name\":\"bob\"},{\"name\":\"bob\"}]"); f = catalog.lookup("asJsonNode", "application/json"); assertThat(new String(f.apply(m).getPayload())).isEqualTo("[{\"name\":\"bob\"},{\"name\":\"bob\"}]"); @@ -263,26 +263,27 @@ public void testDefaultLookup() throws Exception { FunctionCatalog catalog = this.configureCatalog(); Object function = catalog.lookup(""); assertThat(function).isNull(); - //== + // == System.setProperty("spring.cloud.function.definition", "uppercase"); catalog = this.configureCatalog(); function = catalog.lookup(""); assertThat(function).isNotNull(); -// Field field = ReflectionUtils.findField(FunctionInvocationWrapper.class, "composed"); -// field.setAccessible(true); + // Field field = ReflectionUtils.findField(FunctionInvocationWrapper.class, + // "composed"); + // field.setAccessible(true); assertThat(((FunctionInvocationWrapper) function).isComposed()).isFalse(); - //== + // == System.setProperty("spring.cloud.function.definition", "uppercase|uppercaseFlux"); catalog = this.configureCatalog(); -// function = catalog.lookup("", "application/json"); + // function = catalog.lookup("", "application/json"); function = catalog.lookup(""); Function, Flux>> typedFunction = (Function, Flux>>) function; Object blockFirst = typedFunction.apply(Flux.just("hello")).blockFirst(); System.out.println(blockFirst); assertThat(function).isNotNull(); -// field = ReflectionUtils.findField(FunctionInvocationWrapper.class, "composed"); -// field.setAccessible(true); -// assertThat(((boolean) field.get(function))).isTrue(); + // field = ReflectionUtils.findField(FunctionInvocationWrapper.class, "composed"); + // field.setAccessible(true); + // assertThat(((boolean) field.get(function))).isTrue(); assertThat(((FunctionInvocationWrapper) function).isComposed()).isTrue(); } @@ -299,16 +300,22 @@ public void testBiFunction() { public void testImperativeFunction() { FunctionCatalog catalog = this.configureCatalog(); - Function>, Flux>> messageFlux = catalog.lookup("uppercase", "application/json"); - Message message1 = MessageBuilder.withPayload("\"uppercaseFlux\"".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build(); - Message message2 = MessageBuilder.withPayload("\"uppercaseFlux2\"".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build(); + Function>, Flux>> messageFlux = catalog.lookup("uppercase", + "application/json"); + Message message1 = MessageBuilder.withPayload("\"uppercaseFlux\"".getBytes()) + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build(); + Message message2 = MessageBuilder.withPayload("\"uppercaseFlux2\"".getBytes()) + .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") + .build(); List> messageResult = messageFlux.apply(Flux.just(message1, message2)).collectList().block(); assertThat(messageResult.get(0).getPayload()).isEqualTo("\"UPPERCASEFLUX\"".getBytes(StandardCharsets.UTF_8)); assertThat(messageResult.get(1).getPayload()).isEqualTo("\"UPPERCASEFLUX2\"".getBytes(StandardCharsets.UTF_8)); } @Test - public void testConsumerFunction() { // function that returns Void, effectively a Consumer + public void testConsumerFunction() { // function that returns Void, effectively a + // Consumer FunctionCatalog catalog = this.configureCatalog(); Function consumerFunction = catalog.lookup("consumerFunction"); @@ -317,7 +324,8 @@ public void testConsumerFunction() { // function that returns Void, effectively Function, Void> consumerFunctionAsMessageA = catalog.lookup("consumerFunction"); assertThat(consumerFunctionAsMessageA.apply(new GenericMessage("\"hello\"".getBytes()))).isNull(); - Function, Void> consumerFunctionAsMessageB = catalog.lookup("consumerFunction", "application/json"); + Function, Void> consumerFunctionAsMessageB = catalog.lookup("consumerFunction", + "application/json"); assertThat(consumerFunctionAsMessageB.apply(new GenericMessage("\"hello\"".getBytes()))).isNull(); } @@ -325,13 +333,13 @@ public void testConsumerFunction() { // function that returns Void, effectively public void testMessageToPojoConversion() { FunctionCatalog catalog = this.configureCatalog(); Function, Person> uppercasePerson = catalog.lookup("uppercasePerson"); - Person person = uppercasePerson.apply(MessageBuilder.withPayload("{\"name\":\"bill\",\"id\":2}").build()); + Person person = uppercasePerson.apply(MessageBuilder.withPayload("{\"name\":\"bill\",\"id\":2}").build()); assertThat(person.getName()).isEqualTo("BILL"); } /* - * When invoking imperative function as reactive the rules are - * - the input wrapper must match the output wrapper (e.g., or ) + * When invoking imperative function as reactive the rules are - the input wrapper + * must match the output wrapper (e.g., or ) */ @Test public void testImperativeVoidInputFunction() { @@ -369,7 +377,6 @@ public void testReactiveVoidInputFunctionAsSupplier() { assertThat(resultList.get(0)).isEqualTo("voidInputFunctionReactive2"); } - @Test public void testComposition() throws Exception { FunctionCatalog catalog = this.configureCatalog(); @@ -397,7 +404,7 @@ public void testComposition() throws Exception { FunctionInvocationWrapper function = catalog.lookup("uppercase|reverse"); assertThat(function.apply("foo")).isEqualTo("OOF"); - Object target = function.getTarget(); + Object target = function.getTarget(); Field arg1Field = ReflectionUtils.findField(target.getClass(), "arg$1"); arg1Field.setAccessible(true); FunctionInvocationWrapper functionUppercase = (FunctionInvocationWrapper) arg1Field.get(target); @@ -424,9 +431,9 @@ public void testCompositionSupplierAndFunction() { } /* - * This test should fail since the actual function is , hence we can - * not possibly convert Flux (which implies "many") to a single string. - * Further more, such flux will need to be triggered (e.g., subscribe(..) ) + * This test should fail since the actual function is , hence we can not + * possibly convert Flux (which implies "many") to a single string. Further more, such + * flux will need to be triggered (e.g., subscribe(..) ) */ @SuppressWarnings("unused") @Test @@ -445,7 +452,8 @@ public void testReactiveFunctionWithImperativeInputAndOutputFail() { @Test public void testCompositionWithOutputConversion() { FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>> fluxFunction = catalog.lookup("uppercase|reverseFlux", "application/json"); + Function, Flux>> fluxFunction = catalog.lookup("uppercase|reverseFlux", + "application/json"); List> result = fluxFunction.apply(Flux.just("hello", "bye")).collectList().block(); assertThat(result.get(0).getPayload()).isEqualTo("\"OLLEH\"".getBytes()); assertThat(result.get(1).getPayload()).isEqualTo("\"EYB\"".getBytes()); @@ -493,22 +501,23 @@ public void textTypeConversionWithComplexInputType() { assertThat(result).isEqualTo("BIKE"); // as Message - result = (String) function.apply(MessageBuilder.withPayload("{\"key\":\"purchase\",\"data\":{\"name\":\"bike\"}}").build()); + result = (String) function + .apply(MessageBuilder.withPayload("{\"key\":\"purchase\",\"data\":{\"name\":\"bike\"}}").build()); assertThat(result).isEqualTo("BIKE"); // as Message - result = (String) function.apply(MessageBuilder.withPayload("{\"key\":\"purchase\",\"data\":{\"name\":\"bike\"}}".getBytes()).build()); + result = (String) function.apply( + MessageBuilder.withPayload("{\"key\":\"purchase\",\"data\":{\"name\":\"bike\"}}".getBytes()).build()); assertThat(result).isEqualTo("BIKE"); } // MULTI INPUT/OUTPUT - @Test public void testMultiInput() { FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple"); + Function, Flux>, Flux> multiInputFunction = catalog + .lookup("multiInputSingleOutputViaReactiveTuple"); Flux stringStream = Flux.just("one", "two", "three"); Flux intStream = Flux.just(1, 2, 3); @@ -519,12 +528,11 @@ public void testMultiInput() { assertThat(result.get(2)).isEqualTo("three-3"); } - - //@Test + // @Test public void testMultiInputWithComposition() { FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple|uppercase"); + Function, Flux>, Flux> multiInputFunction = catalog + .lookup("multiInputSingleOutputViaReactiveTuple|uppercase"); Flux stringStream = Flux.just("one", "two", "three"); Flux intStream = Flux.just("1", "2", "3"); @@ -535,12 +543,11 @@ public void testMultiInputWithComposition() { assertThat(result.get(2)).isEqualTo("THREE-3"); } - @Test public void testMultiOutput() { FunctionCatalog catalog = this.configureCatalog(); - Function, Tuple3, Flux, Flux>> multiOutputFunction = - catalog.lookup("multiOutputAsTuple"); + Function, Tuple3, Flux, Flux>> multiOutputFunction = catalog + .lookup("multiOutputAsTuple"); Flux personStream = Flux.just(new Person("Uncle Sam", 1), new Person("Oncle Pierre", 2)); Tuple3, Flux, Flux> result = multiOutputFunction.apply(personStream); @@ -585,9 +592,12 @@ public void SCF_GH_429ConfigurationTests() throws Exception { @Disabled public void byteArrayNoSpecialHandling() throws Exception { FunctionCatalog catalog = this.configureCatalog(ByteArrayFunction.class); - FunctionInvocationWrapper function = catalog.lookup("beanFactoryAwareFunctionRegistryTests.ByteArrayFunction", "application/json"); + FunctionInvocationWrapper function = catalog.lookup("beanFactoryAwareFunctionRegistryTests.ByteArrayFunction", + "application/json"); assertThat(function).isNotNull(); - Message result = (Message) function.apply(MessageBuilder.withPayload("hello".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "application/octet-stream").build()); + Message result = (Message) function.apply(MessageBuilder.withPayload("hello".getBytes()) + .setHeader(MessageHeaders.CONTENT_TYPE, "application/octet-stream") + .build()); System.out.println(new String(result.getPayload())); @@ -600,7 +610,9 @@ public void testMultipleValuesInOutputHandling() throws Exception { FunctionCatalog catalog = this.configureCatalog(CollectionOutConfiguration.class); FunctionInvocationWrapper function = catalog.lookup("parseToList", "application/json"); assertThat(function).isNotNull(); - Object result = function.apply(MessageBuilder.withPayload("1,2,3".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build()); + Object result = function.apply(MessageBuilder.withPayload("1,2,3".getBytes()) + .setHeader(MessageHeaders.CONTENT_TYPE, "text/plain") + .build()); assertThat(result instanceof Message).isTrue(); byte[] payload = ((Message) result).getPayload(); JsonMapper mapper = this.context.getBean(JsonMapper.class); @@ -611,7 +623,9 @@ public void testMultipleValuesInOutputHandling() throws Exception { function = catalog.lookup("parseToListOfMessages", "application/json"); assertThat(function).isNotNull(); - result = function.apply(MessageBuilder.withPayload("1,2,3".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build()); + result = function.apply(MessageBuilder.withPayload("1,2,3".getBytes()) + .setHeader(MessageHeaders.CONTENT_TYPE, "text/plain") + .build()); assertThat(result instanceof List).isTrue(); assertThat(((Message) ((List) result).get(0)).getHeaders()).containsKey("foo"); assertThat(((Message) ((List) result).get(1)).getHeaders()).containsKey("foo"); @@ -619,14 +633,17 @@ public void testMultipleValuesInOutputHandling() throws Exception { } /** - * The following two tests test the fallback mechanism when an accept header has several values. - * The function produces Integer, which cannot be serialized by the default converter supporting text/plain - * (StringMessageConverter) but can by the one supporting application/json, which comes second. + * The following two tests test the fallback mechanism when an accept header has + * several values. The function produces Integer, which cannot be serialized by the + * default converter supporting text/plain (StringMessageConverter) but can by the one + * supporting application/json, which comes second. */ - //@Test + // @Test public void testMultipleOrderedAcceptValues() throws Exception { FunctionCatalog catalog = this.configureCatalog(MultipleOrderedAcceptValuesConfiguration.class); - Function> function = catalog.lookup("beanFactoryAwareFunctionRegistryTests.MultipleOrderedAcceptValuesConfiguration", "text/plain,application/json"); + Function> function = catalog.lookup( + "beanFactoryAwareFunctionRegistryTests.MultipleOrderedAcceptValuesConfiguration", + "text/plain,application/json"); assertThat(function).isNotNull(); Message result = function.apply("hello"); assertThat(result.getPayload()).isEqualTo("5".getBytes("UTF-8")); @@ -649,22 +666,20 @@ public void testSerializationWithCompatibleWildcardSubtypeAcceptHeader() { FunctionCatalog catalog = this.configureCatalog(NegotiatingMessageConverterConfiguration.class); FunctionInvocationWrapper function = catalog.lookup("echo", "text/*"); - Message> tupleResult = (Message>) function.apply(MessageBuilder - .withPayload(Tuples.of("bonjour", "monde")) + Message> tupleResult = (Message>) function + .apply(MessageBuilder.withPayload(Tuples.of("bonjour", "monde")) .setHeader(MessageHeaders.CONTENT_TYPE, MimeType.valueOf("text/csv")) - .build() - ); + .build()); assertThat(tupleResult.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeType.valueOf("text/csv")); assertThat(tupleResult.getHeaders().get("accept")).isNull(); - Message dateResult = (Message) function.apply(MessageBuilder - .withPayload(123) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeType.valueOf("text/integer")) - .build() - ); + Message dateResult = (Message) function.apply(MessageBuilder.withPayload(123) + .setHeader(MessageHeaders.CONTENT_TYPE, MimeType.valueOf("text/integer")) + .build()); - assertThat(dateResult.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeType.valueOf("text/integer")); + assertThat(dateResult.getHeaders().get(MessageHeaders.CONTENT_TYPE)) + .isEqualTo(MimeType.valueOf("text/integer")); assertThat(dateResult.getHeaders().get("accept")).isNull(); } @@ -731,9 +746,8 @@ public void testEachElementInFluxIsProcessed() { assertThat(list.get(0).name).isEqualTo("OLEG"); assertThat(list.get(1).name).isEqualTo("SEVA"); - - - result = (Flux) f.apply(new GenericMessage("[{\"id\":1, \"name\":\"oleg\"}, {\"id\":2, \"name\":\"seva\"}]")); + result = (Flux) f + .apply(new GenericMessage("[{\"id\":1, \"name\":\"oleg\"}, {\"id\":2, \"name\":\"seva\"}]")); list = (List) result.collectList().block(); assertThat(list.size()).isEqualTo(2); assertThat(list.get(0).name).isEqualTo("OLEG"); @@ -743,8 +757,7 @@ public void testEachElementInFluxIsProcessed() { @Test public void testGH_608() { ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); Consumer> consumer = catalog.lookup("reactivePojoConsumer"); @@ -757,14 +770,14 @@ public void testGH_608() { public void testGH_611() { FunctionCatalog catalog = this.configureCatalog(NegotiatingMessageConverterConfiguration.class); Supplier> f = catalog.lookup("supplier", "text/*"); - assertThat(f.get().getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeTypeUtils.parseMimeType("text/*")); + assertThat(f.get().getHeaders().get(MessageHeaders.CONTENT_TYPE)) + .isEqualTo(MimeTypeUtils.parseMimeType("text/*")); } @Test public void testGH_608_C() { ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); String productJson = "{\"key\":\"someKey\",\"data\": {\"name\":\"bike\"}}"; @@ -790,14 +803,16 @@ public void testGH_609() { public void testGH_635() throws Exception { FunctionCatalog catalog = this.configureCatalog(SCF_GH_635ConfigurationAsFunction.class); Function lmFunction = catalog.lookup("emptyMessageList", "application/json"); - List> emptyListOfMessages = (List>) lmFunction.apply(MessageBuilder.withPayload("hello").build()); + List> emptyListOfMessages = (List>) lmFunction + .apply(MessageBuilder.withPayload("hello").build()); assertThat(emptyListOfMessages).isEmpty(); emptyListOfMessages = (List>) lmFunction.apply("hello"); assertThat(emptyListOfMessages).isEmpty(); JsonMapper mapper = this.context.getBean(JsonMapper.class); Function lsFunction = catalog.lookup("emptyStringList", "application/json"); - Message emptyListOfString = (Message) lsFunction.apply(MessageBuilder.withPayload("hello").build()); + Message emptyListOfString = (Message) lsFunction + .apply(MessageBuilder.withPayload("hello").build()); List resultList = mapper.fromJson(emptyListOfString.getPayload(), List.class); assertThat(resultList).isEmpty(); emptyListOfString = (Message) lsFunction.apply("hello"); @@ -820,8 +835,8 @@ public void testGH_768() throws Exception { @Test public void test_791() { try (ConfigurableApplicationContext ac = new SpringApplicationBuilder(InputHeaderPropagationConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true")) { + .run("--logging.level.org.springframework.cloud.function=DEBUG", + "--spring.main.lazy-initialization=true")) { FunctionCatalog catalog = ac.getBean(FunctionCatalog.class); Function, Message> uppercase = catalog.lookup("uppercase", "application/json"); @@ -829,9 +844,8 @@ public void test_791() { assertThat(result.getHeaders()).doesNotContainKey("foo"); } try (ConfigurableApplicationContext ac = new SpringApplicationBuilder(InputHeaderPropagationConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.uppercase.copy-input-headers=true")) { + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true", + "--spring.cloud.function.configuration.uppercase.copy-input-headers=true")) { FunctionCatalog catalog = ac.getBean(FunctionCatalog.class); Function, Message> uppercase = catalog.lookup("uppercase", "application/json"); @@ -850,7 +864,8 @@ public void testArrayPayloadOnFluxFunction() throws Exception { list.add("Ricky"); list.add("Julien"); list.add("Bubbles"); - Publisher p = (Publisher) lmFunction.apply(MessageBuilder.withPayload(list).setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); + Publisher p = (Publisher) lmFunction + .apply(MessageBuilder.withPayload(list).setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); List result = new ArrayList<>(); for (Object value : Flux.from(p).toIterable()) { result.add(value); @@ -876,7 +891,8 @@ public void testConcurrencyOnLookup() throws Exception { FunctionCatalog catalog = this.configureCatalog(SampleFunctionConfiguration.class); for (int y = 0; y < 10; y++) { executor.execute(() -> { - assertThat((FunctionInvocationWrapper) catalog.lookup("uppercase|reverse", "application/json")).isNotNull(); + assertThat((FunctionInvocationWrapper) catalog.lookup("uppercase|reverse", "application/json")) + .isNotNull(); counter.incrementAndGet(); }); } @@ -909,11 +925,13 @@ public Function toJson() { public Function, String> uppercasePerson() { return v -> v.getPayload().getName().toUpperCase(Locale.ROOT); } + } @EnableAutoConfiguration @Configuration(proxyBeanMethods = false) public static class JsonNodeConfiguration { + @Bean public Function, String> messageAsJsonNode() { return v -> { @@ -927,6 +945,7 @@ public Function asJsonNode() { return v.toString(); }; } + } @EnableAutoConfiguration @@ -941,10 +960,12 @@ public interface ReactiveFunction extends Function, Flux> { @Component @EnableAutoConfiguration public static class ReactiveFunctionImpl implements ReactiveFunction { + @Override public Flux apply(Flux inFlux) { return inFlux.map(v -> Integer.parseInt(v)); } + } @EnableAutoConfiguration @@ -958,11 +979,14 @@ public Function> parseToList() { @Bean public Function>> parseToListOfMessages() { return v -> { - List> list = Arrays.asList(v.split(",")).stream() - .map(value -> MessageBuilder.withPayload(value).setHeader("foo", "foo").build()).collect(Collectors.toList()); + List> list = Arrays.asList(v.split(",")) + .stream() + .map(value -> MessageBuilder.withPayload(value).setHeader("foo", "foo").build()) + .collect(Collectors.toList()); return list; }; } + } @EnableAutoConfiguration @@ -994,13 +1018,14 @@ public MessageConverter messageConverterC() { } public static class ConverterC extends ConverterA { + ConverterC() { super("text/*"); } @Override - protected Object convertFromInternal( - Message message, Class targetClass, @Nullable Object conversionHint) { + protected Object convertFromInternal(Message message, Class targetClass, + @Nullable Object conversionHint) { return message.getPayload().toString(); } @@ -1019,16 +1044,18 @@ protected boolean canConvertFrom(Message message, @Nullable Class targetCl protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) { return payload instanceof Integer; } + } public static class ConverterB extends ConverterA { + ConverterB() { super("text/integer"); } @Override - protected Object convertFromInternal( - Message message, Class targetClass, @Nullable Object conversionHint) { + protected Object convertFromInternal(Message message, Class targetClass, + @Nullable Object conversionHint) { return message.getPayload().toString(); } @@ -1047,6 +1074,7 @@ protected boolean canConvertFrom(Message message, @Nullable Class targetCl protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) { return payload instanceof String; } + } private static class ConverterA extends AbstractMessageConverter { @@ -1061,8 +1089,8 @@ private static class ConverterA extends AbstractMessageConverter { @SuppressWarnings("unchecked") @Override - protected Object convertFromInternal( - Message message, Class targetClass, @Nullable Object conversionHint) { + protected Object convertFromInternal(Message message, Class targetClass, + @Nullable Object conversionHint) { Tuple2 payload = (Tuple2) message.getPayload(); String convertedPayload = payload.getT1() + "," + payload.getT2(); @@ -1089,15 +1117,20 @@ protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) protected boolean supports(Class clazz) { throw new UnsupportedOperationException(); } + } + } @EnableAutoConfiguration @Configuration protected static class WrappedWithAroundAdviseConfiguration { + @Bean public Function, Message> uppercase() { - return v -> MessageBuilder.withPayload(v.getPayload().toUpperCase(Locale.ROOT)).copyHeaders(v.getHeaders()).build(); + return v -> MessageBuilder.withPayload(v.getPayload().toUpperCase(Locale.ROOT)) + .copyHeaders(v.getHeaders()) + .build(); } @Bean @@ -1111,11 +1144,13 @@ protected Object doApply(Object input, FunctionInvocationWrapper targetFunction) // in this test we know input is a Message Message mInput = (Message) input; MessageBuilder.fromMessage(mInput).setHeader("before", "foo").build(); - Message result = (Message) targetFunction.apply(MessageBuilder.fromMessage(mInput).setHeader("before", "foo").build()); + Message result = (Message) targetFunction + .apply(MessageBuilder.fromMessage(mInput).setHeader("before", "foo").build()); return MessageBuilder.fromMessage(result).setHeader("after", "bar").build(); } }; } + } @EnableAutoConfiguration @@ -1126,6 +1161,7 @@ protected static class InputHeaderPropagationConfiguration { public Function uppercase() { return x -> x.toUpperCase(Locale.ROOT); } + } @EnableAutoConfiguration @@ -1175,7 +1211,9 @@ public Function uppercase() { public Function, Message> uppercaseMessage() { return message -> { Message result = MessageBuilder.fromMessage(message) - .removeHeader("foo").setHeader("bar", "bar").build(); + .removeHeader("foo") + .setHeader("bar", "bar") + .build(); return result; }; } @@ -1220,7 +1258,6 @@ public Function, Flux> reverseFlux() { }); } - @Bean public Function, Mono> monoVoidToMonoVoid() { return mono -> mono.doOnSuccess(v -> System.out.println("HELLO")); @@ -1238,7 +1275,7 @@ public Function, Flux>, Flux> multiInputSin return Flux.zip(stringStream, intStream, (string, integer) -> string + "-" + integer); }; } - //======== + // ======== // MULTI-OUTPUT @Bean @@ -1254,7 +1291,7 @@ public Function, Tuple3, Flux, Flux>> public Function, Flux>> multiOutputAsTuple2() { return null; } - //======== + // ======== @Bean public Function, Mono> monoToMonoVoid() { @@ -1334,6 +1371,7 @@ public Class getObjectType() { }; } + } @EnableAutoConfiguration @@ -1353,6 +1391,7 @@ public Object get() { } } + } @EnableAutoConfiguration @@ -1370,7 +1409,9 @@ public Object apply(Object t) { // TODO Auto-generated method stub return null; } + } + } @EnableAutoConfiguration @@ -1385,48 +1426,67 @@ public Function>> emptyMessageList() { public Function> emptyStringList() { return input -> Collections.emptyList(); } + } @EnableAutoConfiguration public static class SCF_GH_768ConfigurationAsFunction { + @Bean public Function, String> echoToString() { return data -> { for (Entry dataEntry : data.entrySet()) { - assertThat(dataEntry.getValue()).isInstanceOf(Date.class); // would fail if value would not be converted to Person + assertThat(dataEntry.getValue()).isInstanceOf(Date.class); // would + // fail if + // value + // would + // not be + // converted + // to + // Person } return data.toString(); }; } + } public static class Person { + private String name; private int id; + public Person() { } + public Person(String name, int id) { this.name = name; this.id = id; } + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public int getId() { return id; } + public void setId(int id) { this.id = id; } + @Override public String toString() { return "Person: " + name + "/" + id; } + } @EnableAutoConfiguration @@ -1463,6 +1523,7 @@ public byte[] apply(byte[] bytes) { } return result; } + } @EnableAutoConfiguration @@ -1474,12 +1535,14 @@ public static class MultipleOrderedAcceptValuesConfiguration implements Function public Integer apply(String t) { return t.length(); } + } @EnableAutoConfiguration @Configuration(proxyBeanMethods = false) @Component - public static class MultipleOrderedAcceptValuesAsMessageOutputConfiguration implements Function> { + public static class MultipleOrderedAcceptValuesAsMessageOutputConfiguration + implements Function> { @Override public Message apply(String t) { @@ -1491,13 +1554,16 @@ public Message apply(String t) { @EnableAutoConfiguration @Configuration public static class ComplexTypeFunctionConfiguration { + @Bean public Function, String> function() { return v -> v.getData().getName().toUpperCase(Locale.ROOT); } + } private static class Product { + private String name; public String getName() { @@ -1507,6 +1573,7 @@ public String getName() { public void setName(String name) { this.name = name; } + } private static class Event { @@ -1530,7 +1597,9 @@ public V getData() { public void setData(V data) { this.data = data; } + } + @EnableAutoConfiguration @Configuration public static class EmptyPojoConfiguratioin { @@ -1539,6 +1608,7 @@ public static class EmptyPojoConfiguratioin { public Function echo() { return v -> v; } + } public static class EmptyPojo { @@ -1553,6 +1623,7 @@ public static class CompositionWithNullReturnInBetween { public Function echo1() { return v -> null; } + @Bean public Function echo2() { return v -> { @@ -1562,12 +1633,14 @@ public Function echo2() { return v; }; } + } @EnableAutoConfiguration @Configuration // s-c-f-1141 - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings({ "unchecked", "rawtypes" }) public static class CompositionReactiveSupplierWithConsumer { + private static List results = new ArrayList<>(); @Bean @@ -1591,8 +1664,7 @@ public Supplier>> supplyMessage() { @Bean public Supplier> supplyPrimitive() { return () -> { - return Flux.fromArray( - new Integer[] { 1, 2}); + return Flux.fromArray(new Integer[] { 1, 2 }); }; } @@ -1605,6 +1677,7 @@ public Consumer consume() { results.add(v); }; } + } @EnableAutoConfiguration @@ -1615,5 +1688,7 @@ public static class MessageWithArrayAsPayload { public Function, Message> myFunction() { return msg -> msg; } + } + } 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 9d4146574..b4fc99951 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 @@ -34,7 +34,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * */ @@ -42,7 +41,7 @@ public class BeanFactoryAwarePojoFunctionRegistryTests { private FunctionCatalog configureCatalog() { ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG"); + .run("--logging.level.org.springframework.cloud.function=DEBUG"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); return catalog; } @@ -51,8 +50,8 @@ private FunctionCatalog configureCatalog() { public void testWithPojoFunctionImplementingFunction() { FunctionCatalog catalog = this.configureCatalog(); -// MyFunction f1 = catalog.lookup("myFunction"); -// assertThat(f1.uppercase("foo")).isEqualTo("FOO"); + // MyFunction f1 = catalog.lookup("myFunction"); + // assertThat(f1.uppercase("foo")).isEqualTo("FOO"); Function f2 = catalog.lookup("myFunction"); assertThat(f2.apply("foo")).isEqualTo("FOO"); @@ -67,15 +66,16 @@ public void testWithPojoFunctionImplementingFunction() { assertThat(f3.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); Function, Message> f2messageReturned = catalog.lookup("myFunction", "application/json"); - assertThat(new String(f2messageReturned.apply(MessageBuilder.withPayload("message").build()).getPayload())).isEqualTo("\"MESSAGE\""); + assertThat(new String(f2messageReturned.apply(MessageBuilder.withPayload("message").build()).getPayload())) + .isEqualTo("\"MESSAGE\""); } @Test public void testWithPojoFunction() { FunctionCatalog catalog = this.configureCatalog(); -// MyFunctionLike f1 = catalog.lookup("myFunctionLike"); -// assertThat(f1.uppercase("foo")).isEqualTo("FOO"); + // MyFunctionLike f1 = catalog.lookup("myFunctionLike"); + // assertThat(f1.uppercase("foo")).isEqualTo("FOO"); Function f2 = catalog.lookup("myFunctionLike"); assertThat(f2.apply("foo")).isEqualTo("FOO"); @@ -91,8 +91,10 @@ public void testWithPojoFunction() { Function, Flux> f3 = catalog.lookup("myFunctionLike"); assertThat(f3.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); - Function, Message> f2messageReturned = catalog.lookup("myFunctionLike", "application/json"); - assertThat(new String(f2messageReturned.apply(MessageBuilder.withPayload("message").build()).getPayload())).isEqualTo("\"MESSAGE\""); + Function, Message> f2messageReturned = catalog.lookup("myFunctionLike", + "application/json"); + assertThat(new String(f2messageReturned.apply(MessageBuilder.withPayload("message").build()).getPayload())) + .isEqualTo("\"MESSAGE\""); } @Test @@ -112,22 +114,17 @@ public void testPojoFunctionReturnsMessageWithoutContentType() { // Test POJO function without contentType Function, Object> pojoFunction = catalog.lookup("myFunctionLike"); - Message input = MessageBuilder.withPayload("test") - .setHeader("correlationId", "123") - .build(); + Message input = MessageBuilder.withPayload("test").setHeader("correlationId", "123").build(); Object result = pojoFunction.apply(input); // GH-1307: Verify POJO functions return Message for consistency - assertThat(result) - .as("POJO function should return Message, not plain value when input is Message") + assertThat(result).as("POJO function should return Message, not plain value when input is Message") .isInstanceOf(Message.class); Message messageResult = (Message) result; assertThat(messageResult.getPayload()).isEqualTo("TEST"); - assertThat(messageResult.getHeaders().get("correlationId")) - .as("Headers should be preserved") - .isEqualTo("123"); + assertThat(messageResult.getHeaders().get("correlationId")).as("Headers should be preserved").isEqualTo("123"); } /** @@ -148,7 +145,6 @@ public void testPojoFunctionDoesNotWrapPlainStringInput() { .isEqualTo("PLAININPUT"); } - @EnableAutoConfiguration @Configuration(proxyBeanMethods = false) protected static class SampleFunctionConfiguration { @@ -167,10 +163,12 @@ public MyFunctionLike myFunctionLike() { public Function func() { return v -> v; } + } // POJO Function that implements Function private static class MyFunction implements Function { + public String uppercase(String value) { return value.toUpperCase(Locale.ROOT); } @@ -179,12 +177,16 @@ public String uppercase(String value) { public String apply(String t) { return this.uppercase(t); } + } // POJO Function public static class MyFunctionLike { + public String uppercase(String value) { return value.toUpperCase(Locale.ROOT); } + } + } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java index dd3dc0bfa..d6cc7e9d8 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java @@ -16,7 +16,6 @@ package org.springframework.cloud.function.context.catalog; - import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -50,7 +49,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * */ @@ -66,10 +64,10 @@ public void testDiscoverFunctionalMethod() throws Exception { @Test public void testFunctionTypeFrom() throws Exception { Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(SimpleConsumer.class); - //assertThat(type).isInstanceOf(ParameterizedType.class); + // assertThat(type).isInstanceOf(ParameterizedType.class); Type wrapperType = FunctionTypeUtils.getInputType(type); -// Type wrapperType = ((ParameterizedType) type).getActualTypeArguments()[0]; -// assertThat(wrapperType).isInstanceOf(ParameterizedType.class); + // Type wrapperType = ((ParameterizedType) type).getActualTypeArguments()[0]; + // assertThat(wrapperType).isInstanceOf(ParameterizedType.class); assertThat(wrapperType.getTypeName()).contains("Flux"); Type innerWrapperType = ((ParameterizedType) wrapperType).getActualTypeArguments()[0]; @@ -86,39 +84,53 @@ public void testFunctionTypeByClassDiscovery() { assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(Object.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(MessageFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))).isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))) + .isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))) + .isAssignableFrom(String.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))).isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))) + .isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))) + .isAssignableFrom(String.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(MessageConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))) + .isAssignableFrom(String.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))) + .isAssignableFrom(String.class); } @Test public void testWithComplexHierarchy() { Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(ReactiveFunctionImpl.class); - assertThat(String.class).isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))); - assertThat(Integer.class).isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))); + assertThat(String.class) + .isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))); + assertThat(Integer.class) + .isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))); } @Test public void testIsTypeCollection() { - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference() { }.getType())).isFalse(); - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>() { }.getType())).isTrue(); - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>>() { }.getType())).isTrue(); - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>>>() { }.getType())).isTrue(); - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>>>() { }.getType())).isFalse(); + assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference() { + }.getType())).isFalse(); + assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>() { + }.getType())).isTrue(); + assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>>() { + }.getType())).isTrue(); + assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>>>() { + }.getType())).isTrue(); + assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>>>() { + }.getType())).isFalse(); } @Test public void testWithComplexGenericsHierarchy() throws Exception { - Type functionType = FunctionTypeUtils.discoverFunctionTypeFromFunctionFactoryMethod(FunctionTypeUtilsTests.class, "methodWithGenerics"); + Type functionType = FunctionTypeUtils + .discoverFunctionTypeFromFunctionFactoryMethod(FunctionTypeUtilsTests.class, "methodWithGenerics"); Type inputType = FunctionTypeUtils.getInputType(functionType); Class typeClass = FunctionTypeUtils.getRawType(inputType); assertThat(typeClass).isAssignableFrom(Message.class); @@ -131,83 +143,106 @@ public void testWithComplexGenericsHierarchy() throws Exception { assertThat(typeClass).isAssignableFrom(SomeDomainObject.class); } - //@Test + // @Test public void testPrimitiveFunctionInputTypes() { Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(IntConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(IntConsumer.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(IntConsumer.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(IntSupplier.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(IntSupplier.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(IntSupplier.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(IntFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(IntFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(IntFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(ToIntFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(ToIntFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(ToIntFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(LongConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(LongConsumer.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(LongConsumer.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(LongSupplier.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(LongSupplier.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(LongSupplier.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(LongFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(LongFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(LongFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(ToLongFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(ToLongFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(ToLongFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(DoubleConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(DoubleConsumer.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(DoubleConsumer.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(DoubleSupplier.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(DoubleSupplier.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(DoubleSupplier.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(DoubleFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(DoubleFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(DoubleFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(ToDoubleFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(ToDoubleFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + .isAssignableFrom(ToDoubleFunction.class); } - //@Test + // @Test public void testPrimitiveFunctionOutputTypes() { Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(IntConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(IntConsumer.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(IntConsumer.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(IntSupplier.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(IntSupplier.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(IntSupplier.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(IntFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(IntFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(IntFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(ToIntFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(ToIntFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(ToIntFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(LongConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(LongConsumer.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(LongConsumer.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(LongSupplier.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(LongSupplier.class); - + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(LongSupplier.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(LongFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(LongFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(LongFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(ToLongFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(ToLongFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(ToLongFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(DoubleConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(DoubleConsumer.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(DoubleConsumer.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(DoubleSupplier.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(DoubleSupplier.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(DoubleSupplier.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(DoubleFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(DoubleFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(DoubleFunction.class); type = FunctionTypeUtils.discoverFunctionTypeFromClass(ToDoubleFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))).isAssignableFrom(ToDoubleFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))) + .isAssignableFrom(ToDoubleFunction.class); } private static Function function() { @@ -223,13 +258,11 @@ private static Function, Tuple3> return null; } - private static Function, Mono>, - Tuple3, Flux, Mono>> multiInputOutputPublisherFunction() { + private static Function, Mono>, Tuple3, Flux, Mono>> multiInputOutputPublisherFunction() { return null; } - private static Function>, Mono>, - Tuple3>, Flux, Mono>> multiInputOutputPublisherFunctionComplexTypes() { + private static Function>, Mono>, Tuple3>, Flux, Mono>> multiInputOutputPublisherFunctionComplexTypes() { return null; } @@ -267,7 +300,7 @@ public static GenericBatchMessageListConsumer methodWithGeneri return new GenericBatchMessageListConsumer(); } - //============ + // ============ private interface MessageFunction extends Function, Message> { @@ -286,9 +319,11 @@ private interface MyMessageConsumer extends MessageConsumer { } public static class SimpleConsumer implements Consumer>> { + @Override public void accept(Flux> messageFlux) { } + } public interface ReactiveFunction extends Function, Flux> { @@ -301,6 +336,7 @@ public static class ReactiveFunctionImpl implements ReactiveFunction apply(Flux inFlux) { return inFlux.map(v -> Integer.parseInt(v)); } + } public static abstract class AbstractConsumer implements Consumer> { @@ -315,12 +351,15 @@ public final void accept(Message message) { } protected abstract void doAccept(C payload); + } public static class SampleEventConsumer extends AbstractConsumer { + @Override protected void doAccept(SampleData data) { } + } public static class SampleData { @@ -330,12 +369,14 @@ public static class SampleData { public static class SomeDomainObject { } + public static class GenericBatchMessageListConsumer implements Consumer>> { @Override public void accept(Message> listMessage) { } + } } 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 c9b68c188..8350bc249 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 @@ -82,7 +82,6 @@ import org.springframework.util.MimeType; import org.springframework.util.ReflectionUtils; - import static org.assertj.core.api.Assertions.assertThat; /** @@ -109,24 +108,32 @@ public void before() { } @ParameterizedTest - @ValueSource(strings = { - "aaaaaaaaaa", // no problem - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]" // protobuf encoder prepends '[' for length (91 bytes) + @ValueSource(strings = { "aaaaaaaaaa", // no problem + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]" // protobuf + // encoder + // prepends + // '[' + // for + // length + // (91 + // bytes) }) public void testSCF1094(String stringValue) throws IOException { Function getValue = msg -> msg != null ? msg.getValue() : null; - Type functionType = ResolvableType.forClassWithGenerics(Function.class, ResolvableType.forClass(StringValue.class), ResolvableType.forClass(String.class)).getType(); + Type functionType = ResolvableType + .forClassWithGenerics(Function.class, ResolvableType.forClass(StringValue.class), + ResolvableType.forClass(String.class)) + .getType(); - var catalog = new SimpleFunctionRegistry(this.conversionService, new CompositeMessageConverter(List.of(new ProtobufMessageConverter())), new JacksonMapper(new ObjectMapper())); + var catalog = new SimpleFunctionRegistry(this.conversionService, + new CompositeMessageConverter(List.of(new ProtobufMessageConverter())), + new JacksonMapper(new ObjectMapper())); catalog.register(new FunctionRegistration<>(getValue, "getValue").type(functionType)); FunctionInvocationWrapper lookedUpFunction = catalog.lookup("getValue"); ByteArrayOutputStream payload = new ByteArrayOutputStream(); - StringValue.newBuilder() - .setValue(stringValue) - .build() - .writeTo(payload); + StringValue.newBuilder().setValue(stringValue).build().writeTo(payload); var inputMessage = MessageBuilder.withPayload(payload.toByteArray()) .setHeader("contentType", "application/x-protobuf") @@ -139,8 +146,7 @@ public void testSCF1094(String stringValue) throws IOException { @Test public void concurrencyRegistrationTest() throws Exception { Echo function = new Echo(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(Echo.class); + FunctionRegistration registration = new FunctionRegistration<>(function, "echo").type(Echo.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); ExecutorService executor = Executors.newCachedThreadPool(); @@ -159,8 +165,7 @@ public void concurrencyRegistrationTest() throws Exception { @Test public void testCachingOfFunction() { Echo function = new Echo(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(Echo.class); + FunctionRegistration registration = new FunctionRegistration<>(function, "echo").type(Echo.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); @@ -175,8 +180,7 @@ public void testCachingOfFunction() { @Test public void testNoCachingOfFunction() { Echo function = new Echo(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(Echo.class); + FunctionRegistration registration = new FunctionRegistration<>(function, "echo").type(Echo.class); registration.getProperties().put("singleton", "false"); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); @@ -192,17 +196,23 @@ public void testNoCachingOfFunction() { @Test public void testSCF768() { ResolvableType map = ResolvableType.forClassWithGenerics(Map.class, String.class, Person.class); - Type functionType = ResolvableType.forClassWithGenerics(Function.class, map, ResolvableType.forClass(String.class)).getType(); + Type functionType = ResolvableType + .forClassWithGenerics(Function.class, map, ResolvableType.forClass(String.class)) + .getType(); Function, String> function = persons -> { for (Entry entry : persons.entrySet()) { - assertThat(entry.getValue().getName()).isNotEmpty(); // would fail if value would not be converted to Person + assertThat(entry.getValue().getName()).isNotEmpty(); // would fail if + // value would not + // be converted to + // Person } return persons.toString(); }; - FunctionRegistration, String>> registration = new FunctionRegistration<>( - function, "echo").type(functionType); + FunctionRegistration, String>> registration = new FunctionRegistration<>(function, + "echo") + .type(functionType); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); @@ -215,8 +225,7 @@ public void testSCF768() { @Test public void testSCF640() { Echo function = new Echo(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(Echo.class); + FunctionRegistration registration = new FunctionRegistration<>(function, "echo").type(Echo.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); @@ -228,14 +237,15 @@ public void testSCF640() { } @ParameterizedTest - @ValueSource(strings = {"[hello", "hello]", "[hello]"}) + @ValueSource(strings = { "[hello", "hello]", "[hello]" }) void textContentTypeWithValueWrappedBracketsIsOk(String inputMessagePayloadValue) { - var catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); + var catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, + new JacksonMapper(new ObjectMapper())); catalog.register(new FunctionRegistration<>(new Echo(), "echo").type(Echo.class)); FunctionInvocationWrapper lookedUpFunction = catalog.lookup("echo"); var inputMessage = MessageBuilder.withPayload(inputMessagePayloadValue) - .setHeader("contentType", "text/plain") - .build(); + .setHeader("contentType", "text/plain") + .build(); var functionResult = lookedUpFunction.apply(inputMessage); assertThat(functionResult).isEqualTo(inputMessagePayloadValue); } @@ -246,28 +256,31 @@ public void testSCF762() { SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); - FunctionRegistration reg1 = new FunctionRegistration<>( - new UpperCase(), "uppercase").type(UpperCase.class); + FunctionRegistration reg1 = new FunctionRegistration<>(new UpperCase(), "uppercase") + .type(UpperCase.class); catalog.register(reg1); // - FunctionRegistration reg2 = new FunctionRegistration<>( - new UpperCaseMessage(), "uppercaseMessage").type(UpperCaseMessage.class); + FunctionRegistration reg2 = new FunctionRegistration<>(new UpperCaseMessage(), + "uppercaseMessage") + .type(UpperCaseMessage.class); catalog.register(reg2); // - FunctionRegistration reg3 = new FunctionRegistration<>( - new StringArrayFunction(), "stringArray").type(StringArrayFunction.class); + FunctionRegistration reg3 = new FunctionRegistration<>(new StringArrayFunction(), + "stringArray") + .type(StringArrayFunction.class); catalog.register(reg3); // - FunctionRegistration reg4 = new FunctionRegistration<>( - new TypelessFunction(), "typeless").type(TypelessFunction.class); + FunctionRegistration reg4 = new FunctionRegistration<>(new TypelessFunction(), "typeless") + .type(TypelessFunction.class); catalog.register(reg4); // - FunctionRegistration reg5 = new FunctionRegistration<>( - new ByteArrayFunction(), "typeless").type(ByteArrayFunction.class); + FunctionRegistration reg5 = new FunctionRegistration<>(new ByteArrayFunction(), "typeless") + .type(ByteArrayFunction.class); catalog.register(reg5); // - FunctionRegistration reg6 = new FunctionRegistration<>( - new StringListFunction(), "stringList").type(StringListFunction.class); + FunctionRegistration reg6 = new FunctionRegistration<>(new StringListFunction(), + "stringList") + .type(StringListFunction.class); catalog.register(reg6); Message collectionMessage = MessageBuilder.withPayload("[\"ricky\", \"julien\", \"bubbles\"]").build(); @@ -280,7 +293,9 @@ public void testSCF762() { result = lookedUpFunction.apply(collectionMessage); assertThat(result).isInstanceOf(Flux.class); - List> collectionIfResults = Flux.from((Publisher>) result).collectList().block(); + List> collectionIfResults = Flux.from((Publisher>) result) + .collectList() + .block(); assertThat(collectionIfResults.size()).isEqualTo(3); assertThat(collectionIfResults.get(0).getPayload()).isEqualTo("\"RICKY\"".getBytes()); assertThat(collectionIfResults.get(1).getPayload()).isEqualTo("\"JULIEN\"".getBytes()); @@ -292,28 +307,29 @@ public void testSCF762() { result = lookedUpFunction.apply(collectionMessage); assertThat(result).isInstanceOf(Message.class); - assertThat(((Message) result).getPayload()).isEqualTo("[\"ricky\", \"julien\", \"bubbles\"]".getBytes()); - + assertThat(((Message) result).getPayload()) + .isEqualTo("[\"ricky\", \"julien\", \"bubbles\"]".getBytes()); lookedUpFunction = catalog.lookup("stringArray", "application/json"); result = lookedUpFunction.apply(singleValueMessage); assertThat(result).isInstanceOf(Message.class); assertThat(((Message) result).getPayload()).isEqualTo("[\"ricky\"]".getBytes()); -// result = lookedUpFunction.apply(collectionMessage); -// assertThat(result).isInstanceOf(Message.class); -// assertThat(((Message) result).getPayload()).isEqualTo("[ricky, julien, bubbles]".getBytes()); - + // result = lookedUpFunction.apply(collectionMessage); + // assertThat(result).isInstanceOf(Message.class); + // assertThat(((Message) result).getPayload()).isEqualTo("[ricky, julien, + // bubbles]".getBytes()); lookedUpFunction = catalog.lookup("stringList", "application/json"); result = lookedUpFunction.apply(singleValueMessage); assertThat(result).isInstanceOf(Message.class); assertThat(((Message) result).getPayload()).isEqualTo("[\"ricky\"]".getBytes()); -// result = lookedUpFunction.apply(collectionMessage); -// assertThat(result).isInstanceOf(Message.class); -// System.out.println(new String(((Message) result).getPayload())); -// assertThat(((Message) result).getPayload()).isEqualTo("[ricky, julien, bubbles]".getBytes()); + // result = lookedUpFunction.apply(collectionMessage); + // assertThat(result).isInstanceOf(Message.class); + // System.out.println(new String(((Message) result).getPayload())); + // assertThat(((Message) result).getPayload()).isEqualTo("[ricky, julien, + // bubbles]".getBytes()); } @@ -322,16 +338,16 @@ public void testSCF762() { public void testSCF588() { UpperCase function = new UpperCase(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "foo").type(UppercaseFunction.class); + FunctionRegistration registration = new FunctionRegistration<>(function, "foo") + .type(UppercaseFunction.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); FunctionInvocationWrapper lookedUpFunction = catalog.lookup("uppercase"); Message message = MessageBuilder.withPayload("hello") - .setHeader("lambda-runtime-aws-request-id", UUID.randomUUID()) - .build(); + .setHeader("lambda-runtime-aws-request-id", UUID.randomUUID()) + .build(); Object result = lookedUpFunction.apply(message); assertThat(result).isInstanceOf(Message.class); assertThat(((Message) result).getPayload()).isEqualTo("HELLO"); @@ -341,47 +357,45 @@ public void testSCF588() { public void testFunctionLookup() { TestFunction function = new TestFunction(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "foo").type(TestFunction.class); + FunctionRegistration registration = new FunctionRegistration<>(function, "foo") + .type(TestFunction.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); - //FunctionInvocationWrapper lookedUpFunction = catalog.lookup("hello"); + // FunctionInvocationWrapper lookedUpFunction = catalog.lookup("hello"); FunctionInvocationWrapper lookedUpFunction = catalog.lookup("hello"); - assertThat(lookedUpFunction).isNotNull(); // because we only have one and can look it up with any name - FunctionRegistration registration2 = new FunctionRegistration<>( - function, "foo2").type(TestFunction.class); + assertThat(lookedUpFunction).isNotNull(); // because we only have one and can look + // it up with any name + FunctionRegistration registration2 = new FunctionRegistration<>(function, "foo2") + .type(TestFunction.class); catalog.register(registration2); lookedUpFunction = catalog.lookup("hello"); assertThat(lookedUpFunction).isNull(); } - - @Test public void testFunctionComposition() { - FunctionRegistration upperCaseRegistration = new FunctionRegistration<>( - new UpperCase(), "uppercase").type(UpperCase.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); + FunctionRegistration upperCaseRegistration = new FunctionRegistration<>(new UpperCase(), "uppercase") + .type(UpperCase.class); + FunctionRegistration reverseRegistration = new FunctionRegistration<>(new Reverse(), "reverse") + .type(Reverse.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(upperCaseRegistration); catalog.register(reverseRegistration); - Function, Flux> lookedUpFunction = catalog - .lookup("uppercase|reverse"); + Function, Flux> lookedUpFunction = catalog.lookup("uppercase|reverse"); assertThat(lookedUpFunction).isNotNull(); } @Test @Disabled public void testFunctionCompositionImplicit() { - FunctionRegistration wordsRegistration = new FunctionRegistration<>( - new Words(), "words").type(Words.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); + FunctionRegistration wordsRegistration = new FunctionRegistration<>(new Words(), "words") + .type(Words.class); + FunctionRegistration reverseRegistration = new FunctionRegistration<>(new Reverse(), "reverse") + .type(Reverse.class); FunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(wordsRegistration); @@ -397,10 +411,10 @@ public void testFunctionCompositionImplicit() { @Test @Disabled public void testFunctionCompletelyImplicitComposition() { - FunctionRegistration wordsRegistration = new FunctionRegistration<>( - new Words(), "words").type(Words.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); + FunctionRegistration wordsRegistration = new FunctionRegistration<>(new Words(), "words") + .type(Words.class); + FunctionRegistration reverseRegistration = new FunctionRegistration<>(new Reverse(), "reverse") + .type(Reverse.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(wordsRegistration); @@ -415,10 +429,10 @@ public void testFunctionCompletelyImplicitComposition() { @Test public void testFunctionCompositionExplicit() { - FunctionRegistration wordsRegistration = new FunctionRegistration<>( - new Words(), "words").type(Words.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); + FunctionRegistration wordsRegistration = new FunctionRegistration<>(new Words(), "words") + .type(Words.class); + FunctionRegistration reverseRegistration = new FunctionRegistration<>(new Reverse(), "reverse") + .type(Reverse.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(wordsRegistration); @@ -434,38 +448,36 @@ public void testFunctionCompositionExplicit() { public void testFunctionCompositionWithMessages() { FunctionRegistration upperCaseRegistration = new FunctionRegistration<>( new UpperCaseMessage(), "uppercase") - .type(UpperCaseMessage.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new ReverseMessage(), "reverse") - .type(ReverseMessage.class); + .type(UpperCaseMessage.class); + FunctionRegistration reverseRegistration = new FunctionRegistration<>(new ReverseMessage(), + "reverse") + .type(ReverseMessage.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(upperCaseRegistration); catalog.register(reverseRegistration); - Function>, Flux>> lookedUpFunction = catalog - .lookup("uppercase|reverse"); + Function>, Flux>> lookedUpFunction = catalog.lookup("uppercase|reverse"); assertThat(lookedUpFunction).isNotNull(); - assertThat(lookedUpFunction - .apply(Flux.just(MessageBuilder.withPayload("star").build())).blockFirst() - .getPayload()).isEqualTo("RATS"); + assertThat( + lookedUpFunction.apply(Flux.just(MessageBuilder.withPayload("star").build())).blockFirst().getPayload()) + .isEqualTo("RATS"); } @Test public void testFunctionCompositionMixedMessages() { FunctionRegistration upperCaseRegistration = new FunctionRegistration<>( new UpperCaseMessage(), "uppercase") - .type(UpperCaseMessage.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); + .type(UpperCaseMessage.class); + FunctionRegistration reverseRegistration = new FunctionRegistration<>(new Reverse(), "reverse") + .type(Reverse.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(upperCaseRegistration); catalog.register(reverseRegistration); - Function, String> lookedUpFunction = catalog - .lookup("uppercase|reverse"); + Function, String> lookedUpFunction = catalog.lookup("uppercase|reverse"); assertThat(lookedUpFunction).isNotNull(); String result = lookedUpFunction.apply(MessageBuilder.withPayload("star").setHeader("foo", "bar").build()); @@ -475,7 +487,8 @@ public void testFunctionCompositionMixedMessages() { @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testReactiveFunctionMessages() { - FunctionRegistration registration = new FunctionRegistration<>(new ReactiveFunction(), "reactive") + FunctionRegistration registration = new FunctionRegistration<>(new ReactiveFunction(), + "reactive") .type(ReactiveFunction.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, @@ -486,11 +499,9 @@ public void testReactiveFunctionMessages() { assertThat(lookedUpFunction).isNotNull(); Flux> result = (Flux>) lookedUpFunction - .apply(Flux.just(MessageBuilder - .withPayload("[{\"name\":\"item1\"},{\"name\":\"item2\"}]") + .apply(Flux.just(MessageBuilder.withPayload("[{\"name\":\"item1\"},{\"name\":\"item2\"}]") .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") - .build() - )); + .build())); List blockFirst = result.blockFirst(); Assertions.assertThatIterable(blockFirst).isEqualTo(Arrays.asList("item1", "item2")); @@ -501,36 +512,36 @@ public void testReactiveFunctionMessages() { public void testWithCustomMessageConverter() { FunctionCatalog catalog = this.configureCatalog(CustomConverterConfiguration.class); Function function = catalog.lookup("func"); - Object result = function.apply(MessageBuilder.withPayload("Jim Lahey").setHeader(MessageHeaders.CONTENT_TYPE, "text/person").build()); + Object result = function.apply( + MessageBuilder.withPayload("Jim Lahey").setHeader(MessageHeaders.CONTENT_TYPE, "text/person").build()); assertThat(result).isEqualTo("Jim Lahey"); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void lookup() { - SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); + SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, + this.messageConverter, new JacksonMapper(new ObjectMapper())); FunctionInvocationWrapper function = functionRegistry.lookup("uppercase"); assertThat(function).isNull(); Function userFunction = uppercase(); FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "uppercase") - .type(FunctionTypeUtils.functionType(String.class, String.class)); + .type(FunctionTypeUtils.functionType(String.class, String.class)); functionRegistry.register(functionRegistration); function = functionRegistry.lookup("uppercase"); assertThat(function).isNotNull(); } - @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void lookupDefaultName() { - SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); + SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, + this.messageConverter, new JacksonMapper(new ObjectMapper())); Function userFunction = uppercase(); FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "uppercase") - .type(FunctionTypeUtils.functionType(String.class, String.class)); + .type(FunctionTypeUtils.functionType(String.class, String.class)); functionRegistry.register(functionRegistration); FunctionInvocationWrapper function = functionRegistry.lookup(""); @@ -540,17 +551,17 @@ public void lookupDefaultName() { @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void lookupWithCompositionFunctionAndConsumer() { - SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); + SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, + this.messageConverter, new JacksonMapper(new ObjectMapper())); Object userFunction = uppercase(); FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "uppercase") - .type(FunctionTypeUtils.functionType(String.class, String.class)); + .type(FunctionTypeUtils.functionType(String.class, String.class)); functionRegistry.register(functionRegistration); userFunction = consumer(); functionRegistration = new FunctionRegistration(userFunction, "consumer") - .type(ResolvableType.forClassWithGenerics(Consumer.class, Integer.class).getType()); + .type(ResolvableType.forClassWithGenerics(Consumer.class, Integer.class).getType()); functionRegistry.register(functionRegistration); FunctionInvocationWrapper functionWrapper = functionRegistry.lookup("uppercase|consumer"); @@ -561,13 +572,15 @@ public void lookupWithCompositionFunctionAndConsumer() { @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void lookupWithReactiveConsumer() { - SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); + SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, + this.messageConverter, new JacksonMapper(new ObjectMapper())); Object userFunction = reactiveConsumer(); FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "reactiveConsumer") - .type(ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forClassWithGenerics(Flux.class, Integer.class)).getType()); + .type(ResolvableType + .forClassWithGenerics(Consumer.class, ResolvableType.forClassWithGenerics(Flux.class, Integer.class)) + .getType()); functionRegistry.register(functionRegistration); FunctionInvocationWrapper functionWrapper = functionRegistry.lookup("reactiveConsumer"); @@ -577,23 +590,23 @@ public void lookupWithReactiveConsumer() { @Test public void testHeaderEnricherFunction() { - FunctionRegistration registration = - new FunctionRegistration<>(new HeaderEnricherFunction(), "headerEnricher") - .type(HeaderEnricherFunction.class); + FunctionRegistration registration = new FunctionRegistration<>( + new HeaderEnricherFunction(), "headerEnricher") + .type(HeaderEnricherFunction.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); + new JacksonMapper(new ObjectMapper())); catalog.register(registration); Function, Message> function = catalog.lookup("headerEnricher"); - Message message = - function.apply(MessageBuilder.withPayload("hello").setHeader("original", "originalValue") - .build()); + Message message = function + .apply(MessageBuilder.withPayload("hello").setHeader("original", "originalValue").build()); assertThat(message.getHeaders().get("original")).isEqualTo("newValue"); } @Test public void testReactiveMonoSupplier() { FunctionRegistration registration = new FunctionRegistration<>(new ReactiveMonoGreeter(), - "greeter").type(ReactiveMonoGreeter.class); + "greeter") + .type(ReactiveMonoGreeter.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); @@ -612,22 +625,22 @@ public void testHeaderPropagationInComposedFunction() { @Test public void testFunctionCompositionWithReactiveSupplierAndConsumer() { SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); + new JacksonMapper(new ObjectMapper())); Object reactiveFunc = reactiveFluxSupplier(); FunctionRegistration functionRegistration = new FunctionRegistration(reactiveFunc, "reactiveFluxSupplier") - .type(ResolvableType.forClassWithGenerics( - Supplier.class, ResolvableType.forClassWithGenerics(Flux.class, String.class)).getType()); + .type(ResolvableType + .forClassWithGenerics(Supplier.class, ResolvableType.forClassWithGenerics(Flux.class, String.class)) + .getType()); catalog.register(functionRegistration); reactiveFunc = reactiveFluxConsumer(); - functionRegistration = new FunctionRegistration(reactiveFunc, "reactiveFluxConsumer") - .type(ResolvableType.forClassWithGenerics( - Consumer.class, ResolvableType.forClassWithGenerics(Flux.class, String.class)).getType()); + functionRegistration = new FunctionRegistration(reactiveFunc, "reactiveFluxConsumer").type(ResolvableType + .forClassWithGenerics(Consumer.class, ResolvableType.forClassWithGenerics(Flux.class, String.class)) + .getType()); catalog.register(functionRegistration); - FunctionInvocationWrapper lookedUpFunction = catalog - .lookup("reactiveFluxSupplier|reactiveFluxConsumer"); + FunctionInvocationWrapper lookedUpFunction = catalog.lookup("reactiveFluxSupplier|reactiveFluxConsumer"); assertThat(lookedUpFunction).isNotNull(); lookedUpFunction.apply(null); @@ -636,16 +649,18 @@ public void testFunctionCompositionWithReactiveSupplierAndConsumer() { @Test void biConsumerUserFunctionTypeIsKnownInFunctionInvocationWrapper() { - BiConsumer testBiConsumer = (a, b) -> { }; + BiConsumer testBiConsumer = (a, b) -> { + }; Function wrappedFunction = x -> { testBiConsumer.accept(null, null); return null; }; - FunctionRegistration> registration = new FunctionRegistration<>( - wrappedFunction, "functionWrappingBiConsumer").type(Function.class); + FunctionRegistration> registration = new FunctionRegistration<>(wrappedFunction, + "functionWrappingBiConsumer") + .type(Function.class); registration.setUserFunction(testBiConsumer); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); + new JacksonMapper(new ObjectMapper())); catalog.register(registration); FunctionInvocationWrapper functionInvocationWrapper = catalog.lookup("functionWrappingBiConsumer"); @@ -657,7 +672,6 @@ public Function uppercase() { return v -> v.toUpperCase(Locale.ROOT); } - public Function hash() { return v -> v.hashCode(); } @@ -679,9 +693,8 @@ public Consumer> reactiveConsumer() { private final AtomicInteger consumerDowncounter = new AtomicInteger(10); public Supplier> reactiveFluxSupplier() { - return () -> Flux.fromStream( - IntStream.range(0, consumerDowncounter.get()).boxed().map(i -> Integer.toString(i)) - ); + return () -> Flux + .fromStream(IntStream.range(0, consumerDowncounter.get()).boxed().map(i -> Integer.toString(i))); } public Consumer> reactiveFluxConsumer() { @@ -690,20 +703,22 @@ public Consumer> reactiveFluxConsumer() { private FunctionCatalog configureCatalog(Class... configClass) { ApplicationContext context = new SpringApplicationBuilder(configClass) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); return catalog; } @EnableAutoConfiguration private static class CustomConverterConfiguration { + @Bean public MessageConverter stringToPersonConverter() { return new AbstractMessageConverter(MimeType.valueOf("text/person")) { @Override - protected Object convertFromInternal(Message message, Class targetClass, @Nullable Object conversionHint) { - String payload = message.getPayload() instanceof byte[] ? new String((byte[]) message.getPayload()) : (String) message.getPayload(); + protected Object convertFromInternal(Message message, Class targetClass, + @Nullable Object conversionHint) { + String payload = message.getPayload() instanceof byte[] ? new String((byte[]) message.getPayload()) + : (String) message.getPayload(); Person person = new Person(); person.setName(payload); return person; @@ -711,8 +726,8 @@ protected Object convertFromInternal(Message message, Class targetClass, @ @Override protected boolean canConvertFrom(Message message, @Nullable Class targetClass) { - return supportsMimeType(message.getHeaders()) && Person.class.isAssignableFrom(targetClass) && ( - message.getPayload() instanceof String || message.getPayload() instanceof byte[]); + return supportsMimeType(message.getHeaders()) && Person.class.isAssignableFrom(targetClass) + && (message.getPayload() instanceof String || message.getPayload() instanceof byte[]); } @Override @@ -736,9 +751,11 @@ protected boolean supports(Class clazz) { public Function func() { return person -> person.getName(); } + } public static class Person { + private String name; public String getName() { @@ -753,6 +770,7 @@ public void setName(String name) { public String toString() { return this.name; } + } private static class Words implements Supplier { @@ -782,13 +800,13 @@ public Object apply(Object t) { } - private static class UpperCaseMessage - implements Function, Message> { + private static class UpperCaseMessage implements Function, Message> { @Override public Message apply(Message t) { return MessageBuilder.withPayload(t.getPayload().toUpperCase(Locale.ROOT)) - .copyHeaders(t.getHeaders()).build(); + .copyHeaders(t.getHeaders()) + .build(); } } @@ -802,14 +820,13 @@ public String apply(String t) { } - private static class ReverseMessage - implements Function, Message> { + private static class ReverseMessage implements Function, Message> { @Override public Message apply(Message t) { - return MessageBuilder - .withPayload(new StringBuilder(t.getPayload()).reverse().toString()) - .copyHeaders(t.getHeaders()).build(); + return MessageBuilder.withPayload(new StringBuilder(t.getPayload()).reverse().toString()) + .copyHeaders(t.getHeaders()) + .build(); } } @@ -827,10 +844,10 @@ private static class ReactiveFunction implements Function> apply(Flux>> listFlux) { - return listFlux - .map(Message::getPayload) + return listFlux.map(Message::getPayload) .map(lst -> lst.stream().map(Person::getName).collect(Collectors.toList())); } + } private static class ReactiveMonoGreeter implements Supplier>> { @@ -846,37 +863,45 @@ private static class HeaderEnricherFunction implements Function, Mess @Override public Message apply(Message message) { - return MessageBuilder.withPayload(message.getPayload()).setHeader("original", "newValue") - .build(); + return MessageBuilder.withPayload(message.getPayload()).setHeader("original", "newValue").build(); } + } private static class StringArrayFunction implements Function { + @Override public String apply(String[] t) { return Arrays.asList(t).toString(); } + } private static class StringListFunction implements Function, String> { + @Override public String apply(List t) { return t.toString(); } + } private static class TypelessFunction implements Function { + @Override public String apply(Object t) { return t.toString(); } + } private static class ByteArrayFunction implements Function { + @Override public String apply(byte[] t) { return new String(t); } + } @EnableAutoConfiguration @@ -897,6 +922,7 @@ Function reverse() { Consumer> print() { return msg -> assertThat(msg.getHeaders().get("FOO")).isEqualTo("BAR"); } + } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationConditionalLoadingTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationConditionalLoadingTests.java index 12662ba34..eb65d0679 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationConditionalLoadingTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationConditionalLoadingTests.java @@ -32,7 +32,8 @@ import static org.mockito.Mockito.mock; /** - * Tests the conditional loading aspects of the {@link ContextFunctionCatalogAutoConfiguration}. + * Tests the conditional loading aspects of the + * {@link ContextFunctionCatalogAutoConfiguration}. * * @author Chris Bono */ @@ -58,16 +59,18 @@ void cloudEventsMessageConverterBeanLoadedWhenCloudEventsOnClasspath() { @Test void cloudEventsMessageConverterBeanNotLoadedWhenCloudEventsNotOnClasspath() { - contextRunner.withClassLoader(new FilteredClassLoader(CloudEventMessageConverter.class)).run((context) -> - assertThat(context).doesNotHaveBean(CloudEventMessageConverter.class)); + contextRunner.withClassLoader(new FilteredClassLoader(CloudEventMessageConverter.class)) + .run((context) -> assertThat(context).doesNotHaveBean(CloudEventMessageConverter.class)); } @Test void customCloudEventsMessageConverterIsRespected() { CloudEventMessageConverter customConverter = mock(CloudEventMessageConverter.class); contextRunner.withBean(CloudEventMessageConverter.class, () -> customConverter) - .run((context) -> assertThat(context).getBean(CloudEventMessageConverter.class).isSameAs(customConverter)); + .run((context) -> assertThat(context).getBean(CloudEventMessageConverter.class) + .isSameAs(customConverter)); } + } @Nested @@ -75,30 +78,36 @@ class PlainFunctionScanConfig { @Test void functionScanConfigEnabledByDefault() { - contextRunner.withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName()) + contextRunner + .withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName()) .run((context) -> assertThat(context).hasSingleBean(TestFunction.class)); } @Test void functionScanConfigEnabledWhenEnabledPropertySetToTrue() { - contextRunner.withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName(), - "spring.cloud.function.scan.enabled:true") + contextRunner + .withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName(), + "spring.cloud.function.scan.enabled:true") .run((context) -> assertThat(context).hasSingleBean(TestFunction.class)); } @Test void functionScanConfigEnabledWithScanPackagesPointingToNoFunctions() { - contextRunner.withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName() + ".faux", - "spring.cloud.function.scan.enabled:true") + contextRunner + .withPropertyValues( + "spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName() + ".faux", + "spring.cloud.function.scan.enabled:true") .run((context) -> assertThat(context).doesNotHaveBean(TestFunction.class)); } @Test void functionScanConfigDisabledWhenEnabledPropertySetToFalse() { - contextRunner.withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName(), - "spring.cloud.function.scan.enabled:false") + contextRunner + .withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName(), + "spring.cloud.function.scan.enabled:false") .run((context) -> assertThat(context).doesNotHaveBean(TestFunction.class)); } } + } 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 fad905158..00f38fa08 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 @@ -90,19 +90,15 @@ public void close() { public void lookUps() { create(SimpleConfiguration.class); assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")) - .isInstanceOf(Function.class); + assertThat((Function) this.catalog.lookup(Function.class, "function")).isInstanceOf(Function.class); assertThat(this.context.getBean("function2")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, - "function,function2")).isInstanceOf(Function.class); - Function, Flux> f = this.catalog.lookup(Function.class, - "function,function2,function3"); + assertThat((Function) this.catalog.lookup(Function.class, "function,function2")) + .isInstanceOf(Function.class); + Function, Flux> f = this.catalog.lookup(Function.class, "function,function2,function3"); assertThat(f).isInstanceOf(Function.class); - assertThat(f.apply(Flux.just("hello")).blockFirst()) - .isEqualTo("HELLOfunction2function3"); + assertThat(f.apply(Flux.just("hello")).blockFirst()).isEqualTo("HELLOfunction2function3"); assertThat(this.context.getBean("supplierFoo")).isInstanceOf(Supplier.class); - assertThat((Supplier) this.catalog.lookup(Supplier.class, "supplierFoo")) - .isInstanceOf(Supplier.class); + assertThat((Supplier) this.catalog.lookup(Supplier.class, "supplierFoo")).isInstanceOf(Supplier.class); assertThat(this.context.getBean("supplier_Foo")).isInstanceOf(Supplier.class); } @@ -111,11 +107,10 @@ public void lookUps() { public void ambiguousFunction() { create(AmbiguousConfiguration.class); assertThat(this.context.getBean("foos")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "foos")) - .isInstanceOf(Function.class); - assertThat((Supplier) this.catalog.lookup(Supplier.class, "foos")) - .isInstanceOf(Supplier.class); - Class inputType = ((FunctionInvocationWrapper) this.catalog.lookup(Function.class, "foos")).getRawInputType(); + assertThat((Function) this.catalog.lookup(Function.class, "foos")).isInstanceOf(Function.class); + assertThat((Supplier) this.catalog.lookup(Supplier.class, "foos")).isInstanceOf(Supplier.class); + Class inputType = ((FunctionInvocationWrapper) this.catalog.lookup(Function.class, "foos")) + .getRawInputType(); assertThat(inputType).isEqualTo(String.class); FunctionInvocationWrapper function = this.catalog.lookup("foos"); Type outputType = function.getOutputType(); @@ -138,9 +133,9 @@ public void configurationFunction() { public void dependencyInjection() { create(DependencyInjectionConfiguration.class); assertThat(this.context.getBean("foos")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "foos")) - .isInstanceOf(Function.class); - Class inputType = ((FunctionInvocationWrapper) this.catalog.lookup(Function.class, "foos")).getRawInputType(); + assertThat((Function) this.catalog.lookup(Function.class, "foos")).isInstanceOf(Function.class); + Class inputType = ((FunctionInvocationWrapper) this.catalog.lookup(Function.class, "foos")) + .getRawInputType(); assertThat(inputType).isEqualTo(String.class); } @@ -148,9 +143,9 @@ public void dependencyInjection() { public void externalDependencyInjection() { create(ExternalDependencyConfiguration.class); assertThat(this.context.getBean("foos")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "foos")) - .isInstanceOf(Function.class); - Class inputType = ((FunctionInvocationWrapper) this.catalog.lookup(Function.class, "foos")).getRawInputType(); + assertThat((Function) this.catalog.lookup(Function.class, "foos")).isInstanceOf(Function.class); + Class inputType = ((FunctionInvocationWrapper) this.catalog.lookup(Function.class, "foos")) + .getRawInputType(); assertThat(inputType).isEqualTo(String.class); } @@ -206,8 +201,11 @@ public void fluxMessageFunction() { final Type inputType = function.getInputType(); assertThat(FunctionTypeUtils.getRawType(inputType)).isAssignableFrom(Flux.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Message.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(FunctionTypeUtils.getGenericType(inputType)))).isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))) + .isAssignableFrom(Message.class); + assertThat(FunctionTypeUtils + .getRawType(FunctionTypeUtils.getGenericType(FunctionTypeUtils.getGenericType(inputType)))) + .isAssignableFrom(String.class); } @Test @@ -221,8 +219,11 @@ public void publisherMessageFunction() { final Type inputType = function.getInputType(); assertThat(FunctionTypeUtils.getRawType(inputType)).isAssignableFrom(Publisher.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Message.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(FunctionTypeUtils.getGenericType(inputType)))).isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))) + .isAssignableFrom(Message.class); + assertThat(FunctionTypeUtils + .getRawType(FunctionTypeUtils.getGenericType(FunctionTypeUtils.getGenericType(inputType)))) + .isAssignableFrom(String.class); } @@ -270,7 +271,8 @@ public void genericFluxFunction() { FunctionInvocationWrapper function = this.catalog.lookup("function"); assertThat(function).isInstanceOf(Function.class); Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Map.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))) + .isAssignableFrom(Map.class); assertThat(FunctionTypeUtils.getRawType(inputType)).isAssignableFrom(Flux.class); } @@ -281,7 +283,8 @@ public void externalFunction() { FunctionInvocationWrapper function = this.catalog.lookup("function"); assertThat(function).isInstanceOf(Function.class); Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Map.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))) + .isAssignableFrom(Map.class); } @Test @@ -293,7 +296,8 @@ public void singletonFunction() { assertThat(function.isInputTypePublisher()).isFalse(); assertThat(function.isOutputTypePublisher()).isFalse(); Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Integer.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))) + .isAssignableFrom(Integer.class); } @Test @@ -325,7 +329,8 @@ public void componentScanBeanFunction() { FunctionInvocationWrapper function = this.catalog.lookup("function"); assertThat(function).isInstanceOf(Function.class); Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Map.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))) + .isAssignableFrom(Map.class); } @Test @@ -335,7 +340,8 @@ public void componentScanFunction() { FunctionInvocationWrapper function = this.catalog.lookup("function"); assertThat(function).isInstanceOf(Function.class); Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Map.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))) + .isAssignableFrom(Map.class); } @Test @@ -356,8 +362,7 @@ public void componentScanJarFunction() { private void create(String jarfile, Class config, String... props) { try { URL[] urls = new URL[] { new ClassPathResource(jarfile).getURL() }; - ClassUtils.overrideThreadContextClassLoader( - new URLClassLoader(urls, getClass().getClassLoader())); + ClassUtils.overrideThreadContextClassLoader(new URLClassLoader(urls, getClass().getClassLoader())); create(config, props); } catch (Exception e) { @@ -370,8 +375,7 @@ public void simpleFunction() { create(SimpleConfiguration.class); Object bean = this.context.getBean("function"); assertThat(bean).isInstanceOf(Function.class); - Function, Flux> function = this.catalog - .lookup(Function.class, "function"); + Function, Flux> function = this.catalog.lookup(Function.class, "function"); assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); assertThat(bean).isNotSameAs(function); } @@ -389,8 +393,7 @@ public void simpleSupplier() { public void simpleConsumer() { create(SimpleConfiguration.class); assertThat(this.context.getBean("consumer")).isInstanceOf(Consumer.class); - Function, Mono> consumer = this.catalog.lookup(Function.class, - "consumer"); + Function, Mono> consumer = this.catalog.lookup(Function.class, "consumer"); consumer.apply(Flux.just("foo", "bar")).subscribe(); assertThat(this.context.getBean(SimpleConfiguration.class).list).hasSize(2); } @@ -402,18 +405,17 @@ public void qualifiedBean() { assertThat(this.context.getBean("function")).isInstanceOf(Function.class); assertThat((Function) this.catalog.lookup("function")).isNull(); assertThat((Function) this.catalog.lookup("other")).isNotNull(); - assertThat(FunctionTypeUtils.getGenericType(((FunctionInvocationWrapper) this.catalog.lookup("other")).getInputType())) - .isEqualTo(String.class); + assertThat(FunctionTypeUtils + .getGenericType(((FunctionInvocationWrapper) this.catalog.lookup("other")).getInputType())) + .isEqualTo(String.class); } @Test public void aliasBean() { create(AliasConfiguration.class); assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")) - .isNotNull(); - assertThat((Function) this.catalog.lookup(Function.class, "other")) - .isInstanceOf(Function.class); + assertThat((Function) this.catalog.lookup(Function.class, "function")).isNotNull(); + assertThat((Function) this.catalog.lookup(Function.class, "other")).isInstanceOf(Function.class); } @Test @@ -423,29 +425,26 @@ public void registrationBean() { assertThat(this.context.getBean("function")).isInstanceOf(Function.class); assertThat((Function) this.catalog.lookup(Function.class, "function")).isInstanceOf(Function.class); assertThat((Function) this.catalog.lookup(Function.class, "registration")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "other")) - .isInstanceOf(Function.class); + assertThat((Function) this.catalog.lookup(Function.class, "other")).isInstanceOf(Function.class); } -// @Test -// public void factoryBeanFunction() { -// create(FactoryBeanConfiguration.class); -// assertThat(this.context.getBean("function")).isInstanceOf(Function.class); -// assertThat((Function) this.catalog.lookup(Function.class, "function")) -// .isInstanceOf(Function.class); -// Function, Flux> f = this.catalog.lookup(Function.class, -// "function"); -// assertThat(f.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO-bar"); -// } + // @Test + // public void factoryBeanFunction() { + // create(FactoryBeanConfiguration.class); + // assertThat(this.context.getBean("function")).isInstanceOf(Function.class); + // assertThat((Function) this.catalog.lookup(Function.class, "function")) + // .isInstanceOf(Function.class); + // Function, Flux> f = this.catalog.lookup(Function.class, + // "function"); + // assertThat(f.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO-bar"); + // } @Test public void functionCatalogDependentBeanFactoryPostProcessor() { - create(new Class[]{ComponentFunctionConfiguration.class, AppendFunction.class}); + create(new Class[] { ComponentFunctionConfiguration.class, AppendFunction.class }); assertThat(this.context.getBean("appendFunction")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "appendFunction")) - .isInstanceOf(Function.class); - Function, Flux> f = this.catalog.lookup(Function.class, - "appendFunction"); + assertThat((Function) this.catalog.lookup(Function.class, "appendFunction")).isInstanceOf(Function.class); + Function, Flux> f = this.catalog.lookup(Function.class, "appendFunction"); assertThat(f.apply(Flux.just("World")).blockFirst()).isEqualTo("Hello World"); } @@ -456,7 +455,7 @@ private void create(Class type, String... props) { private void create(Class[] types, String... props) { this.context = new SpringApplicationBuilder(types).properties(props).run(); this.catalog = this.context.getBean(FunctionCatalog.class); -// this.inspector = this.context.getBean(FunctionInspector.class); + // this.inspector = this.context.getBean(FunctionInspector.class); } @EnableAutoConfiguration @@ -508,6 +507,7 @@ public Consumer consumer() { @EnableAutoConfiguration @Configuration protected static class ComponentFunctionConfiguration { + @Bean public String value() { return "Hello "; @@ -515,13 +515,16 @@ public String value() { @Bean public BeanFactoryPostProcessor someBeanFactoryPostProcessor(Environment environment, - @Nullable FunctionRegistry functionCatalog) { - return beanFactory -> { }; + @Nullable FunctionRegistry functionCatalog) { + return beanFactory -> { + }; } + } @Component("appendFunction") public static class AppendFunction implements Function { + private String value; public AppendFunction(String value) { @@ -532,6 +535,7 @@ public AppendFunction(String value) { public String apply(String s) { return this.value + s; } + } @EnableAutoConfiguration @@ -552,8 +556,7 @@ public String value() { @EnableAutoConfiguration @Configuration(proxyBeanMethods = false, value = "foos") - protected static class FunctionConfiguration - implements Function, Flux> { + protected static class FunctionConfiguration implements Function, Flux> { @Override public Flux apply(Flux flux) { @@ -628,8 +631,9 @@ 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(Locale.ROOT))); + return m -> m.entrySet() + .stream() + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().toString().toUpperCase(Locale.ROOT))); } } @@ -646,8 +650,7 @@ protected static class ExternalConfiguration { protected static class SingletonConfiguration implements BeanFactoryPostProcessor { @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { beanFactory.registerSingleton("function", new SingletonFunction()); } @@ -655,12 +658,10 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) @EnableAutoConfiguration @Configuration - protected static class SingletonMessageConfiguration - implements BeanFactoryPostProcessor { + protected static class SingletonMessageConfiguration implements BeanFactoryPostProcessor { @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { beanFactory.registerSingleton("function", new SingletonMessageFunction()); } @@ -686,8 +687,7 @@ public String apply(Integer input) { } - protected static class SingletonMessageFunction - implements Function, Message> { + protected static class SingletonMessageFunction implements Function, Message> { @Override public Message apply(Message input) { @@ -722,8 +722,9 @@ 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(Locale.ROOT)))); + return flux -> flux.map(m -> m.entrySet() + .stream() + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().toString().toUpperCase(Locale.ROOT)))); } } @@ -734,8 +735,7 @@ protected static class FluxMessageConfiguration { @Bean public Function>, Flux>> function() { - return flux -> flux.map(m -> MessageBuilder - .withPayload(m.getPayload().toUpperCase(Locale.ROOT)).build()); + return flux -> flux.map(m -> MessageBuilder.withPayload(m.getPayload().toUpperCase(Locale.ROOT)).build()); } } @@ -746,8 +746,8 @@ protected static class PublisherMessageConfiguration { @Bean public Function>, Publisher>> function() { - return flux -> Flux.from(flux).map(m -> MessageBuilder - .withPayload(m.getPayload().toUpperCase(Locale.ROOT)).build()); + return flux -> Flux.from(flux) + .map(m -> MessageBuilder.withPayload(m.getPayload().toUpperCase(Locale.ROOT)).build()); } } @@ -758,8 +758,7 @@ protected static class MonoConfiguration { @Bean public Function, Mono>> function() { - return flux -> flux.collect(HashMap::new, - (map, word) -> map.merge(word, 1, Integer::sum)); + return flux -> flux.collect(HashMap::new, (map, word) -> map.merge(word, 1, Integer::sum)); } } @@ -815,8 +814,7 @@ protected static class RegistrationConfiguration { @Bean public FunctionRegistration> registration() { - return new FunctionRegistration>(function(), - "other"); + return new FunctionRegistration>(function(), "other"); } @Bean @@ -826,30 +824,29 @@ public Function function() { } -// @EnableAutoConfiguration -// @Configuration(proxyBeanMethods = false ) -// protected static class FactoryBeanConfiguration -// implements BeanDefinitionRegistryPostProcessor { -// -// @Override -// public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) -// throws BeansException { -// RootBeanDefinition beanDefinition = new RootBeanDefinition( -// FunctionFactoryBean.class); -// beanDefinition.setSource(new DescriptiveResource("Function")); -// registry.registerBeanDefinition("function", beanDefinition); -// } -// -// @Override -// public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) -// throws BeansException { -// -// } -// -// } - - private static class FunctionFactoryBean - extends AbstractFactoryBean> { + // @EnableAutoConfiguration + // @Configuration(proxyBeanMethods = false ) + // protected static class FactoryBeanConfiguration + // implements BeanDefinitionRegistryPostProcessor { + // + // @Override + // public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) + // throws BeansException { + // RootBeanDefinition beanDefinition = new RootBeanDefinition( + // FunctionFactoryBean.class); + // beanDefinition.setSource(new DescriptiveResource("Function")); + // registry.registerBeanDefinition("function", beanDefinition); + // } + // + // @Override + // public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + // throws BeansException { + // + // } + // + // } + + private static class FunctionFactoryBean extends AbstractFactoryBean> { @Override public Class getObjectType() { 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 6380aac35..eb7f2dce1 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 @@ -70,20 +70,17 @@ public void close() { @Test public void lookUps() { create(SimpleConfiguration.class); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")) - .isInstanceOf(Function.class); + assertThat(this.context.getBean("function")).isInstanceOf(FunctionRegistration.class); + assertThat((Function) this.catalog.lookup(Function.class, "function")).isInstanceOf(Function.class); } @Test public void properties() { create(PropertiesConfiguration.class, "app.greeting=hello"); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); + assertThat(this.context.getBean("function")).isInstanceOf(FunctionRegistration.class); @SuppressWarnings("unchecked") Function, Flux> function = (Function, Flux>) this.catalog - .lookup(Function.class, "function"); + .lookup(Function.class, "function"); assertThat(function).isInstanceOf(Function.class); assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("hello foo"); } @@ -91,11 +88,10 @@ public void properties() { @Test public void value() { create(ValueConfiguration.class, "app.greeting=hello"); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); + assertThat(this.context.getBean("function")).isInstanceOf(FunctionRegistration.class); @SuppressWarnings("unchecked") Function, Flux> function = (Function, Flux>) this.catalog - .lookup(Function.class, "function"); + .lookup(Function.class, "function"); assertThat(function).isInstanceOf(Function.class); assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("hello foo"); } @@ -104,11 +100,10 @@ public void value() { @Disabled public void compose() { create(SimpleConfiguration.class); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); + assertThat(this.context.getBean("function")).isInstanceOf(FunctionRegistration.class); @SuppressWarnings("unchecked") - Supplier> supplier = (Supplier>) this.catalog - .lookup(Supplier.class, "supplier|function"); + Supplier> supplier = (Supplier>) this.catalog.lookup(Supplier.class, + "supplier|function"); assertThat(supplier).isInstanceOf(Supplier.class); assertThat(supplier.get().blockFirst()).isEqualTo("HELLO"); // TODO: support for function composition @@ -129,8 +124,7 @@ public void missingType() { public void dependencyInjection() { create(DependencyInjectionConfiguration.class); assertThat(this.context.getBean("foos")).isInstanceOf(FunctionRegistration.class); - assertThat((Function) this.catalog.lookup(Function.class, "foos")) - .isInstanceOf(Function.class); + assertThat((Function) this.catalog.lookup(Function.class, "foos")).isInstanceOf(Function.class); } @Test @@ -138,8 +132,7 @@ public void simpleFunction() { create(SimpleConfiguration.class); Object bean = this.context.getBean("function"); assertThat(bean).isInstanceOf(FunctionRegistration.class); - Function, Flux> function - = this.catalog.lookup(Function.class, "function"); + Function, Flux> function = this.catalog.lookup(Function.class, "function"); assertThat(function.apply(Flux.just("{\"name\":\"foo\"}")).blockFirst().getName()).isEqualTo("FOO"); assertThat(bean).isNotSameAs(function); } @@ -150,8 +143,8 @@ public void scanFunction() { "spring.cloud.function.scan.packages=org.springframework.cloud.function.context.scan"); Object bean = this.context.getBean(TestFunction.class.getName()); assertThat(bean).isInstanceOf(Function.class); - Function, Flux> function = this.catalog - .lookup(Function.class, TestFunction.class.getName()); + Function, Flux> function = this.catalog.lookup(Function.class, + TestFunction.class.getName()); assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); assertThat(bean).isNotSameAs(function); } @@ -159,8 +152,7 @@ public void scanFunction() { @Test public void simpleSupplier() { create(SimpleConfiguration.class); - assertThat(this.context.getBean("supplier")) - .isInstanceOf(FunctionRegistration.class); + assertThat(this.context.getBean("supplier")).isInstanceOf(FunctionRegistration.class); Supplier supplier = this.catalog.lookup(Supplier.class, "supplier"); assertThat(supplier.get()).isEqualTo("hello"); } @@ -168,10 +160,8 @@ public void simpleSupplier() { @Test public void simpleConsumer() { create(SimpleConfiguration.class); - assertThat(this.context.getBean("consumer")) - .isInstanceOf(FunctionRegistration.class); - Function, Mono> consumer = this.catalog.lookup(Function.class, - "consumer"); + assertThat(this.context.getBean("consumer")).isInstanceOf(FunctionRegistration.class); + Function, Mono> consumer = this.catalog.lookup(Function.class, "consumer"); consumer.apply(Flux.just("foo", "bar")).subscribe(); assertThat(this.context.getBean(SimpleConfiguration.class).list).hasSize(2); } @@ -185,15 +175,12 @@ public void overrideGson() { } @SuppressWarnings("unchecked") - private void create( - Class> type, + private void create(Class> type, String... props) { - create(Arrays.asList(BeanUtils.instantiateClass(type)) - .toArray(new ApplicationContextInitializer[0]), props); + create(Arrays.asList(BeanUtils.instantiateClass(type)).toArray(new ApplicationContextInitializer[0]), props); } - private void create(ApplicationContextInitializer[] types, - String... props) { + private void create(ApplicationContextInitializer[] types, String... props) { this.context = new GenericApplicationContext(); Map map = new HashMap<>(); for (String prop : props) { @@ -203,20 +190,18 @@ private void create(ApplicationContextInitializer[] t map.put(key, value); } if (!map.isEmpty()) { - this.context.getEnvironment().getPropertySources() - .addFirst(new MapPropertySource("testProperties", map)); + this.context.getEnvironment().getPropertySources().addFirst(new MapPropertySource("testProperties", map)); } for (ApplicationContextInitializer type : types) { type.initialize(this.context); } - new ContextFunctionCatalogInitializer.ContextFunctionCatalogBeanRegistrar( - this.context).postProcessBeanDefinitionRegistry(this.context); + new ContextFunctionCatalogInitializer.ContextFunctionCatalogBeanRegistrar(this.context) + .postProcessBeanDefinitionRegistry(this.context); this.context.refresh(); this.catalog = this.context.getBean(FunctionCatalog.class); } - protected static class EmptyConfiguration - implements ApplicationContextInitializer { + protected static class EmptyConfiguration implements ApplicationContextInitializer { @Override public void initialize(GenericApplicationContext applicationContext) { @@ -229,8 +214,7 @@ protected static class MissingTypeConfiguration @Override public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function())); + context.registerBean("function", FunctionRegistration.class, () -> new FunctionRegistration<>(function())); } @Bean @@ -240,23 +224,19 @@ public Function function() { } - protected static class SimpleConfiguration - implements ApplicationContextInitializer { + protected static class SimpleConfiguration implements ApplicationContextInitializer { private List list = new ArrayList<>(); - @Override public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function()).type(FunctionTypeUtils.functionType(Person.class, Person.class))); + context.registerBean("function", FunctionRegistration.class, () -> new FunctionRegistration<>(function()) + .type(FunctionTypeUtils.functionType(Person.class, Person.class))); context.registerBean("supplier", FunctionRegistration.class, - () -> new FunctionRegistration<>(supplier()) - .type(FunctionTypeUtils.supplierType(String.class))); + () -> new FunctionRegistration<>(supplier()).type(FunctionTypeUtils.supplierType(String.class))); context.registerBean("consumer", FunctionRegistration.class, - () -> new FunctionRegistration<>(consumer()) - .type(FunctionTypeUtils.consumerType(String.class))); + () -> new FunctionRegistration<>(consumer()).type(FunctionTypeUtils.consumerType(String.class))); context.registerBean(SimpleConfiguration.class, () -> this); } @@ -278,11 +258,11 @@ public Supplier supplier() { public Consumer consumer() { return value -> this.list.add(value); } + } @ConfigurationProperties("app") - protected static class PropertiesConfiguration - implements ApplicationContextInitializer { + protected static class PropertiesConfiguration implements ApplicationContextInitializer { private String greeting; @@ -296,8 +276,8 @@ public void setGreeting(String greeting) { @Override public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function()).type(FunctionTypeUtils.functionType(String.class, String.class))); + context.registerBean("function", FunctionRegistration.class, () -> new FunctionRegistration<>(function()) + .type(FunctionTypeUtils.functionType(String.class, String.class))); context.registerBean(PropertiesConfiguration.class, () -> this); } @@ -308,16 +288,15 @@ public Function function() { } - protected static class ValueConfiguration - implements ApplicationContextInitializer { + protected static class ValueConfiguration implements ApplicationContextInitializer { @Value("${app.greeting}") private String greeting; @Override public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function()).type(FunctionTypeUtils.functionType(String.class, String.class))); + context.registerBean("function", FunctionRegistration.class, () -> new FunctionRegistration<>(function()) + .type(FunctionTypeUtils.functionType(String.class, String.class))); context.registerBean(ValueConfiguration.class, () -> this); } @@ -328,8 +307,7 @@ public Function function() { } - protected static class GsonConfiguration - implements ApplicationContextInitializer { + protected static class GsonConfiguration implements ApplicationContextInitializer { private Gson gson = new Gson(); @@ -354,7 +332,7 @@ public void initialize(GenericApplicationContext context) { context.registerBean(String.class, () -> value()); context.registerBean("foos", FunctionRegistration.class, () -> new FunctionRegistration<>(foos(context.getBean(String.class))) - .type(FunctionTypeUtils.functionType(String.class, Foo.class))); + .type(FunctionTypeUtils.functionType(String.class, Foo.class))); } @Bean @@ -370,15 +348,13 @@ public String value() { } protected static class FunctionConfiguration - implements Function, Flux>, - ApplicationContextInitializer { + implements Function, Flux>, ApplicationContextInitializer { @Override public void initialize(GenericApplicationContext context) { context.registerBean("foos", FunctionConfiguration.class, () -> this); context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(this, "foos") - .type(FunctionConfiguration.class)); + () -> new FunctionRegistration<>(this, "foos").type(FunctionConfiguration.class)); } @Override @@ -415,6 +391,7 @@ public void setValue(String value) { } private static class Person { + private String name; public String getName() { @@ -424,5 +401,7 @@ public String getName() { public void setName(String name) { this.name = name; } + } + } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/JsonMessageConverterTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/JsonMessageConverterTests.java index 2bf933f20..2a141ddee 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/JsonMessageConverterTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/JsonMessageConverterTests.java @@ -25,11 +25,9 @@ import org.springframework.messaging.support.MessageBuilder; import org.springframework.util.MimeTypeUtils; - import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * */ @@ -44,14 +42,18 @@ public void testTypeInference() { assertThat(converter.canConvertFrom(message, Object.class)).isFalse(); assertThat(converter.canConvertFrom(message, null)).isFalse(); - message = MessageBuilder.withPayload("{\"name\":\"bill\"}").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build(); + message = MessageBuilder.withPayload("{\"name\":\"bill\"}") + .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) + .build(); assertThat(converter.canConvertFrom(message, Person.class)).isTrue(); assertThat(converter.canConvertFrom(message, Object.class)).isFalse(); assertThat(converter.canConvertFrom(message, null)).isFalse(); assertThat(converter.convertFromInternal(message, Person.class, null)).isInstanceOf(Person.class); message = MessageBuilder.withPayload("{\"name\":\"bill\"}") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON.toString() + ";type=" + Person.class.getName()).build(); + .setHeader(MessageHeaders.CONTENT_TYPE, + MimeTypeUtils.APPLICATION_JSON.toString() + ";type=" + Person.class.getName()) + .build(); assertThat(converter.canConvertFrom(message, Object.class)).isTrue(); assertThat(converter.canConvertFrom(message, null)).isTrue(); assertThat(converter.convertFromInternal(message, Person.class, null)).isInstanceOf(Person.class); @@ -60,6 +62,7 @@ public void testTypeInference() { } public static class Person { + private String name; public String getName() { @@ -69,5 +72,7 @@ public String getName() { public void setName(String name) { this.name = name; } + } + } 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 074e767ec..ba7d4f44c 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 @@ -45,7 +45,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * */ @@ -77,7 +76,8 @@ private FunctionCatalog configureCatalog() { @Test public void testDefaultRouting() { Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "blah").build(); + .setHeader(FunctionProperties.PREFIX + ".definition", "blah") + .build(); FunctionCatalog functionCatalog = this.configureCatalog(EmptyConfiguration.class); Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); @@ -94,7 +94,8 @@ public void testDefaultRouting() { function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); function.apply(message); - ConfigurationWithDefaultMessageRoutingHandler config = this.context.getBean(ConfigurationWithDefaultMessageRoutingHandler.class); + ConfigurationWithDefaultMessageRoutingHandler config = this.context + .getBean(ConfigurationWithDefaultMessageRoutingHandler.class); assertThat(config.defaultHandlerInvoked).isTrue(); } @@ -105,7 +106,8 @@ public void testInvocationWithMessageAndStringHeader() { Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "reverse").build(); + .setHeader(FunctionProperties.PREFIX + ".definition", "reverse") + .build(); assertThat(function.apply(message)).isEqualTo("olleh"); } @@ -116,8 +118,8 @@ public void testInvocationWithMessageAndListOfSingleElementHeader() { Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", List.of("reverse")) - .build(); + .setHeader(FunctionProperties.PREFIX + ".definition", List.of("reverse")) + .build(); assertThat(function.apply(message)).isEqualTo("olleh"); } @@ -128,9 +130,8 @@ public void testCompositionWithMessageAndListOfMultipleElementsHeader() { Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", - List.of("reverse", "uppercase")) - .build(); + .setHeader(FunctionProperties.PREFIX + ".definition", List.of("reverse", "uppercase")) + .build(); assertThat(function.apply(message)).isEqualTo("OLLEH"); } @@ -141,9 +142,8 @@ public void testInvocationWithMessageAndListOfSingleRoutingExpression() { Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".routing-expression", - List.of("'reverse'")) - .build(); + .setHeader(FunctionProperties.PREFIX + ".routing-expression", List.of("'reverse'")) + .build(); assertThat(function.apply(message)).isEqualTo("olleh"); } @@ -154,9 +154,8 @@ public void testInvocationWithMessageAndListOfMultipleRoutingExpressions() { Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".routing-expression", - List.of("'uppercase'", "'reverse'")) - .build(); + .setHeader(FunctionProperties.PREFIX + ".routing-expression", List.of("'uppercase'", "'reverse'")) + .build(); assertThat(function.apply(message)).isEqualTo("HELLO"); } @@ -167,7 +166,8 @@ public void testRoutingSimpleInputWithReactiveFunctionWithMessageHeader() { Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "echoFlux").build(); + .setHeader(FunctionProperties.PREFIX + ".definition", "echoFlux") + .build(); assertThat(((Flux) function.apply(message)).blockFirst()).isEqualTo("hello"); } @@ -178,7 +178,8 @@ public void testRoutingReactiveInputWithReactiveFunctionAndDefinitionMessageHead Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "echoFlux").build(); + .setHeader(FunctionProperties.PREFIX + ".definition", "echoFlux") + .build(); Flux resultFlux = (Flux) function.apply(Flux.just(message)); StepVerifier.create(resultFlux).expectError().verify(); @@ -191,7 +192,8 @@ public void testRoutingReactiveInputWithReactiveFunctionAndExpressionMessageHead Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".routing-expression", "'echoFlux'").build(); + .setHeader(FunctionProperties.PREFIX + ".routing-expression", "'echoFlux'") + .build(); Flux resultFlux = (Flux) function.apply(Flux.just(message)); StepVerifier.create(resultFlux).expectError().verify(); } @@ -203,9 +205,9 @@ public void failWithHeaderProvidedExpressionAccessingRuntime() { Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".routing-expression", - "T(java.lang.Runtime).getRuntime().exec(\"open -a calculator.app\")") - .build(); + .setHeader(FunctionProperties.PREFIX + ".routing-expression", + "T(java.lang.Runtime).getRuntime().exec(\"open -a calculator.app\")") + .build(); try { function.apply(message); Assertions.fail(); @@ -281,7 +283,8 @@ public void testOtherExpectedFailures() { // non existing function try { function.apply(MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "blah").build()); + .setHeader(FunctionProperties.PREFIX + ".definition", "blah") + .build()); Assertions.fail(); } catch (Exception e) { @@ -298,7 +301,8 @@ public void testInvocationWithMessageComposed() { assertThat(function).isNotNull(); Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "uppercase").build(); + .setHeader(FunctionProperties.PREFIX + ".definition", "uppercase") + .build(); assertThat(function.apply(message)).isEqualTo("OLLEH"); } @@ -325,7 +329,9 @@ public void testMultipleRoutersCaseInsensitiveKeys() { FunctionCatalog functionCatalog = this.configureCatalog(MultipleRouterConfiguration.class); Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello").setHeader(FunctionProperties.PREFIX + ".DeFiNition", "uppercase").build(); + Message message = MessageBuilder.withPayload("hello") + .setHeader(FunctionProperties.PREFIX + ".DeFiNition", "uppercase") + .build(); assertThat(function.apply(message)).isEqualTo("HELLO"); } @@ -347,6 +353,7 @@ public Function uppercase() { public Function, Flux> echoFlux() { return f -> f; } + } @EnableAutoConfiguration @@ -354,10 +361,12 @@ public Function, Flux> echoFlux() { protected static class MultipleRouterConfiguration { @Bean - RoutingFunction mySpecialRouter(FunctionCatalog functionCatalog, BeanFactory beanFactory, @Nullable MessageRoutingCallback routingCallback) { + RoutingFunction mySpecialRouter(FunctionCatalog functionCatalog, BeanFactory beanFactory, + @Nullable MessageRoutingCallback routingCallback) { Map propertiesMap = new HashMap<>(); propertiesMap.put(FunctionProperties.PREFIX + ".routing-expression", "'reverse'"); - return new RoutingFunction(functionCatalog, propertiesMap, new BeanFactoryResolver(beanFactory), routingCallback); + return new RoutingFunction(functionCatalog, propertiesMap, new BeanFactoryResolver(beanFactory), + routingCallback); } @Bean @@ -369,17 +378,21 @@ public Function reverse() { public Function uppercase() { return String::toUpperCase; } + } @EnableAutoConfiguration @Configuration protected static class EmptyConfiguration { + } @EnableAutoConfiguration @Configuration protected static class ConfigurationWithDefaultMessageRoutingHandler { + public boolean defaultHandlerInvoked; + @Bean public DefaultMessageRoutingHandler defaultRoutingHandler() { return new DefaultMessageRoutingHandler() { @@ -390,5 +403,7 @@ public void accept(Message message) { } }; } + } + } 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 b7cca6f74..ae7d803b1 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 @@ -35,7 +35,8 @@ * */ // @checkstyle:off -@FunctionalSpringBootTest(classes = Object.class, properties = "spring.main.sources=org.springframework.cloud.function.context.string.FunctionalStringSourceTests.TestConfiguration") +@FunctionalSpringBootTest(classes = Object.class, + properties = "spring.main.sources=org.springframework.cloud.function.context.string.FunctionalStringSourceTests.TestConfiguration") // @checkstyle:on public class FunctionalStringSourceTests { @@ -44,8 +45,7 @@ public class FunctionalStringSourceTests { @Test public void words() throws Exception { - Function, Flux> function = this.catalog - .lookup(Function.class, "function"); + Function, Flux> function = this.catalog.lookup(Function.class, "function"); assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); } 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 48f1f5863..b494e8c2f 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 @@ -40,8 +40,7 @@ public class FunctionalTests { @Test public void words() throws Exception { - Function, Flux> function = this.catalog - .lookup(Function.class, "function"); + Function, Flux> function = this.catalog.lookup(Function.class, "function"); assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); } 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 59521a79a..4ca85fd43 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 @@ -28,13 +28,13 @@ * */ @Component("function") -public class ScannedFunction - implements Function, Map> { +public class ScannedFunction implements Function, Map> { @Override public Map apply(Map m) { - return m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), - e -> e.getValue().toString().toUpperCase(Locale.ROOT))); + return m.entrySet() + .stream() + .collect(Collectors.toMap(e -> e.getKey(), 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 a5c20509e..f77583b31 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 @@ -33,8 +33,9 @@ public class GenericFunction { @Bean public Function, Map> function() { - return m -> m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), - e -> e.getValue().toString().toUpperCase(Locale.ROOT))); + return m -> m.entrySet() + .stream() + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().toString().toUpperCase(Locale.ROOT))); } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/userissues/UserIssuesTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/userissues/UserIssuesTests.java index 1bb2aa78d..11491958a 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/userissues/UserIssuesTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/userissues/UserIssuesTests.java @@ -37,20 +37,17 @@ import org.springframework.messaging.Message; import org.springframework.messaging.support.GenericMessage; - - import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * */ public class UserIssuesTests { private FunctionCatalog configureCatalog(Class... configClass) { - ApplicationContext context = new SpringApplicationBuilder(configClass).run( - "--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); + ApplicationContext context = new SpringApplicationBuilder(configClass) + .run("--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); FunctionCatalog catalog = context.getBean(FunctionCatalog.class); return catalog; } @@ -64,8 +61,8 @@ public void before() { public void testIssue602() throws Exception { FunctionCatalog catalog = this.configureCatalog(Issue602Configuration.class); Function, Integer> function = catalog.lookup("consumer"); - int result = function.apply( - new GenericMessage("[{\"name\":\"julien\"},{\"name\":\"ricky\"},{\"name\":\"bubbles\"}]")); + int result = function + .apply(new GenericMessage("[{\"name\":\"julien\"},{\"name\":\"ricky\"},{\"name\":\"bubbles\"}]")); assertThat(result).isEqualTo(3); } @@ -74,21 +71,16 @@ public void testIssue602() throws Exception { public void testIssue1075() throws Exception { FunctionCatalog catalog = this.configureCatalog(Issue1075StreamConfiguration.class); - - List list = Arrays.asList(new Product[] {new Product("foo"), new Product("bar")}); + List list = Arrays.asList(new Product[] { new Product("foo"), new Product("bar") }); Event event = new Event(list); EventHolder eventHolder = new EventHolder(event); ObjectMapper mapper = new ObjectMapper(); String message = mapper.writeValueAsString(eventHolder); Function function = catalog.lookup("somethingYouShouldNeverDo"); - boolean result = (boolean) function.apply( - new GenericMessage(message)); + boolean result = (boolean) function.apply(new GenericMessage(message)); assertThat(result).isTrue(); } - - - @Test public void testIssue602asPOJO() throws Exception { FunctionCatalog catalog = this.configureCatalog(Issue602Configuration.class); @@ -127,9 +119,9 @@ public void testIssue601() throws Exception { FunctionCatalog catalog = this.configureCatalog(Issue601Configuration.class); FunctionInvocationWrapper function = catalog.lookup("uppercase"); assertThat(function.getInputType().getTypeName()) - .isEqualTo(ResolvableType.forClassWithGenerics(Flux.class, String.class).getType().getTypeName()); + .isEqualTo(ResolvableType.forClassWithGenerics(Flux.class, String.class).getType().getTypeName()); assertThat(function.getOutputType().getTypeName()) - .isEqualTo(ResolvableType.forClassWithGenerics(Flux.class, Integer.class).getType().getTypeName()); + .isEqualTo(ResolvableType.forClassWithGenerics(Flux.class, Integer.class).getType().getTypeName()); Flux result = (Flux) function.apply(Flux.just("julien", "ricky", "bubbles")); List results = result.collectList().block(); assertThat(results.get(0)).isEqualTo(6); @@ -140,6 +132,7 @@ public void testIssue601() throws Exception { @EnableAutoConfiguration @Configuration public static class Issue602Configuration { + @Bean public Function, Integer> consumer() { return v -> { @@ -149,11 +142,13 @@ public Function, Integer> consumer() { return v.size(); }; } + } @EnableAutoConfiguration @Configuration public static class Issue1075StreamConfiguration { + @Bean public Function>>>, Boolean> somethingYouShouldNeverDo() { return message -> { @@ -163,9 +158,11 @@ public Function>>>, Boolean> somethingYo return true; }; } + } public static class EventHolder { + private T payload; public EventHolder() { @@ -186,6 +183,7 @@ public void setPayload(T payload) { } public static class Event { + private T payload; public Event() { @@ -202,15 +200,18 @@ public T getPayload() { public void setPayload(T payload) { this.payload = payload; } + } @EnableAutoConfiguration @Configuration public static class Issue601Configuration { + @Bean public Uppercase uppercase() { return new Uppercase(); } + } public static class Uppercase implements Function, Flux> { @@ -219,9 +220,11 @@ public static class Uppercase implements Function, Flux> { public Flux apply(Flux s) { return s.map(v -> v.length()); } + } public static class Product { + private String name; public Product() { @@ -238,5 +241,7 @@ public String getName() { public void setName(String name) { this.name = name; } + } + } 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 5439b9e33..32711fdac 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 @@ -30,7 +30,6 @@ import tools.jackson.databind.node.JsonNodeFactory; import tools.jackson.databind.node.ObjectNode; - import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.cloud.function.json.GsonMapper; @@ -60,8 +59,8 @@ public void objectNode_isJsonStringRepresentsCollection() { node.put("foo", "bar"); /* - * Passing the ObjectNode directly results in a positive identification as - * a collection, as its distant parent JsonNode implements Iterable. + * Passing the ObjectNode directly results in a positive identification as a + * collection, as its distant parent JsonNode implements Iterable. */ assertThat(JsonMapper.isJsonStringRepresentsCollection(node)).isFalse(); @@ -69,8 +68,8 @@ public void objectNode_isJsonStringRepresentsCollection() { /* * Sending the node as a string returns false, however, as the line - * isJsonString(value) && str.startsWith("[") && str.endsWith("]") - * will not be true. + * isJsonString(value) && str.startsWith("[") && str.endsWith("]") will not be + * true. */ assertThat(JsonMapper.isJsonStringRepresentsCollection(nodeAsString)).isFalse(); } @@ -89,8 +88,7 @@ public void testJsonDateTimeConversion() { @MethodSource("params") public void vanillaArray(JsonMapper mapper) { String json = "[{\"value\":\"foo\"},{\"value\":\"foo\"}]"; - List list = mapper.fromJson(json, - ResolvableType.forClassWithGenerics(List.class, Foo.class).getType()); + List list = mapper.fromJson(json, ResolvableType.forClassWithGenerics(List.class, Foo.class).getType()); assertThat(list).hasSize(2); assertThat(list.get(0).getValue()).isEqualTo("foo"); assertThat(mapper.toString(list)).isEqualTo(json); @@ -108,8 +106,7 @@ public void intArray(JsonMapper mapper) { @ParameterizedTest @MethodSource("params") public void emptyArray(JsonMapper mapper) { - List list = mapper.fromJson("[]", - ResolvableType.forClassWithGenerics(List.class, Foo.class).getType()); + List list = mapper.fromJson("[]", ResolvableType.forClassWithGenerics(List.class, Foo.class).getType()); assertThat(list).hasSize(0); } @@ -201,5 +198,7 @@ public ZonedDateTime getZonedDateTime() { public void setZonedDateTime(ZonedDateTime zonedDateTime) { this.zonedDateTime = zonedDateTime; } + } + } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMaskerTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMaskerTests.java index a72b0bb24..df8bd4c54 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMaskerTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMaskerTests.java @@ -29,168 +29,62 @@ import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.json.JsonMapper; - import org.springframework.cloud.function.json.JacksonMapper; import org.springframework.util.ReflectionUtils; - import static org.assertj.core.api.Assertions.assertThat; - public class JsonMaskerTests { - private String event = "{\n" - + " \"Records\": [\n" - + " {\n" - + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e69f274ee\",\n" - + " \"eventName\": \"INSERT\",\n" - + " \"eventVersion\": \"1.1\",\n" - + " \"eventSource\": \"aws:dynamodb\",\n" - + " \"awsRegion\": \"us-east-1\",\n" - + " \"userIdentity\":{\n" - + " \"type\":\"Service\",\n" - + " \"principalId\":\"dynamodb.amazonaws.com\"\n" - + " },\n" - + " \"dynamodb\": {\n" - + " \"ApproximateCreationDateTime\": 1.684934517E9,\n" - + " \"Keys\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " }\n" - + " },\n" - + " \"NewImage\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"asdf1\": {\n" - + " \"B\": \"AAEqQQ==\"\n" - + " },\n" - + " \"asdf2\": {\n" - + " \"BS\": [\n" - + " \"AAEqQQ==\",\n" - + " \"QSoBAA==\"\n" - + " ]\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " }\n" - + " },\n" - + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" - + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" - + " },\n" + private String event = "{\n" + " \"Records\": [\n" + " {\n" + + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e69f274ee\",\n" + " \"eventName\": \"INSERT\",\n" + + " \"eventVersion\": \"1.1\",\n" + " \"eventSource\": \"aws:dynamodb\",\n" + + " \"awsRegion\": \"us-east-1\",\n" + " \"userIdentity\":{\n" + " \"type\":\"Service\",\n" + + " \"principalId\":\"dynamodb.amazonaws.com\"\n" + " },\n" + " \"dynamodb\": {\n" + + " \"ApproximateCreationDateTime\": 1.684934517E9,\n" + " \"Keys\": {\n" + + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + " \"key\": {\n" + + " \"S\": \"binary\"\n" + " }\n" + " },\n" + " \"NewImage\": {\n" + + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + " \"asdf1\": {\n" + + " \"B\": \"AAEqQQ==\"\n" + " },\n" + " \"asdf2\": {\n" + + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"QSoBAA==\"\n" + + " ]\n" + " },\n" + " \"key\": {\n" + " \"S\": \"binary\"\n" + + " }\n" + " },\n" + " \"SequenceNumber\": \"1405400000000002063282832\",\n" + + " \"SizeBytes\": 54,\n" + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" + " },\n" + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " },\n" - + " {\n" - + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e42f274ee\",\n" - + " \"eventName\": \"INSERT\",\n" - + " \"eventVersion\": \"1.1\",\n" - + " \"eventSource\": \"aws:dynamodb\",\n" - + " \"awsRegion\": \"us-east-1\",\n" - + " \"dynamodb\": {\n" - + " \"ApproximateCreationDateTime\": 1480642020,\n" - + " \"Keys\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " }\n" - + " },\n" - + " \"NewImage\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"asdf1\": {\n" - + " \"B\": \"AAEqQQ==\"\n" - + " },\n" - + " \"b2\": {\n" - + " \"B\": \"test\"\n" - + " },\n" - + " \"asdf2\": {\n" - + " \"BS\": [\n" - + " \"AAEqQQ==\",\n" - + " \"QSoBAA==\",\n" - + " \"AAEqQQ==\"\n" - + " ]\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " },\n" - + " \"Binary\": {\n" - + " \"B\": \"AAEqQQ==\"\n" - + " },\n" - + " \"Boolean\": {\n" - + " \"BOOL\": true\n" - + " },\n" - + " \"BinarySet\": {\n" - + " \"BS\": [\n" - + " \"AAEqQQ==\",\n" - + " \"AAEqQQ==\"\n" - + " ]\n" - + " },\n" - + " \"List\": {\n" - + " \"L\": [\n" - + " {\n" - + " \"S\": \"Cookies\"\n" - + " },\n" - + " {\n" - + " \"S\": \"Coffee\"\n" - + " },\n" - + " {\n" - + " \"N\": \"3.14159\"\n" - + " }\n" - + " ]\n" - + " },\n" - + " \"Map\": {\n" - + " \"M\": {\n" - + " \"Name\": {\n" - + " \"S\": \"Joe\"\n" - + " },\n" - + " \"Age\": {\n" - + " \"N\": \"35\"\n" - + " }\n" - + " }\n" - + " },\n" - + " \"FloatNumber\": {\n" - + " \"N\": \"123.45\"\n" - + " },\n" - + " \"IntegerNumber\": {\n" - + " \"N\": \"123\"\n" - + " },\n" - + " \"NumberSet\": {\n" - + " \"NS\": [\n" - + " \"1234\",\n" - + " \"567.8\"\n" - + " ]\n" - + " },\n" - + " \"Null\": {\n" - + " \"NULL\": true\n" - + " },\n" - + " \"String\": {\n" - + " \"S\": \"Hello\"\n" - + " },\n" - + " \"StringSet\": {\n" - + " \"SS\": [\n" - + " \"Giraffe\",\n" - + " \"Zebra\"\n" - + " ]\n" - + " },\n" - + " \"EmptyStringSet\": {\n" - + " \"SS\": []\n" - + " }\n" - + " },\n" - + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" - + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" - + " },\n" + + " },\n" + " {\n" + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e42f274ee\",\n" + + " \"eventName\": \"INSERT\",\n" + " \"eventVersion\": \"1.1\",\n" + + " \"eventSource\": \"aws:dynamodb\",\n" + " \"awsRegion\": \"us-east-1\",\n" + + " \"dynamodb\": {\n" + " \"ApproximateCreationDateTime\": 1480642020,\n" + + " \"Keys\": {\n" + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + + " \"key\": {\n" + " \"S\": \"binary\"\n" + " }\n" + " },\n" + + " \"NewImage\": {\n" + " \"val\": {\n" + " \"S\": \"data\"\n" + + " },\n" + " \"asdf1\": {\n" + " \"B\": \"AAEqQQ==\"\n" + " },\n" + + " \"b2\": {\n" + " \"B\": \"test\"\n" + " },\n" + " \"asdf2\": {\n" + + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"QSoBAA==\",\n" + + " \"AAEqQQ==\"\n" + " ]\n" + " },\n" + " \"key\": {\n" + + " \"S\": \"binary\"\n" + " },\n" + " \"Binary\": {\n" + + " \"B\": \"AAEqQQ==\"\n" + " },\n" + " \"Boolean\": {\n" + + " \"BOOL\": true\n" + " },\n" + " \"BinarySet\": {\n" + + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"AAEqQQ==\"\n" + + " ]\n" + " },\n" + " \"List\": {\n" + " \"L\": [\n" + + " {\n" + " \"S\": \"Cookies\"\n" + " },\n" + " {\n" + + " \"S\": \"Coffee\"\n" + " },\n" + " {\n" + + " \"N\": \"3.14159\"\n" + " }\n" + " ]\n" + " },\n" + + " \"Map\": {\n" + " \"M\": {\n" + " \"Name\": {\n" + + " \"S\": \"Joe\"\n" + " },\n" + " \"Age\": {\n" + + " \"N\": \"35\"\n" + " }\n" + " }\n" + " },\n" + + " \"FloatNumber\": {\n" + " \"N\": \"123.45\"\n" + " },\n" + + " \"IntegerNumber\": {\n" + " \"N\": \"123\"\n" + " },\n" + + " \"NumberSet\": {\n" + " \"NS\": [\n" + " \"1234\",\n" + + " \"567.8\"\n" + " ]\n" + " },\n" + " \"Null\": {\n" + + " \"NULL\": true\n" + " },\n" + " \"String\": {\n" + + " \"S\": \"Hello\"\n" + " },\n" + " \"StringSet\": {\n" + + " \"SS\": [\n" + " \"Giraffe\",\n" + " \"Zebra\"\n" + + " ]\n" + " },\n" + " \"EmptyStringSet\": {\n" + " \"SS\": []\n" + + " }\n" + " },\n" + " \"SequenceNumber\": \"1405400000000002063282832\",\n" + + " \"SizeBytes\": 54,\n" + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" + " },\n" + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " }\n" - + " ]\n" - + "}"; + + " }\n" + " ]\n" + "}"; private List maskedKeys = new ArrayList<>(); @@ -281,4 +175,5 @@ else if (entry.getValue() instanceof Collection) { this.iterate(entry.getValue(), keysToMask); } } + } 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 8fa792e95..6e8bc6bfc 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 @@ -14,12 +14,9 @@ * limitations under the License. */ - package org.springframework.cloud.function.core; - /** - * * @author Oleg Zhurakousky * @author John Blum * @since 3.1 @@ -38,4 +35,5 @@ default I preProcessInput(I input, Object inputConverter) { default Object postProcessResult(Object result, I input) { return result; } + } diff --git a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowAutoConfiguration.java b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowAutoConfiguration.java index dcf90bfbe..171eedb36 100644 --- a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowAutoConfiguration.java +++ b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowAutoConfiguration.java @@ -22,11 +22,10 @@ import org.springframework.context.annotation.Bean; /** - * The auto-configuration to expose a {@link FunctionFlowBuilder} bean - * based on the auto-configured {@link FunctionCatalog}. + * The auto-configuration to expose a {@link FunctionFlowBuilder} bean based on the + * auto-configured {@link FunctionCatalog}. * * @author Artem Bilan - * * @since 4.0.3 */ @AutoConfiguration(after = ContextFunctionCatalogAutoConfiguration.class) diff --git a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowBuilder.java b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowBuilder.java index 158780ecf..98d702fdf 100644 --- a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowBuilder.java +++ b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowBuilder.java @@ -39,19 +39,20 @@ import org.springframework.util.Assert; /** - * The entry point for starting a {@link FunctionFlowDefinition}. - * Requires a {@link FunctionCatalog} to lookup function instances - * by their names or definitions from respective operators. + * The entry point for starting a {@link FunctionFlowDefinition}. Requires a + * {@link FunctionCatalog} to lookup function instances by their names or definitions from + * respective operators. *

- * In addition to standard {@link IntegrationFlow} {@code from()} overloaded methods (for convenience), - * this class introduces {@link #fromSupplier(String)} factory methods to resolve the target {@link Supplier} - * by its name or function definition from the provided {@link FunctionCatalog}. + * In addition to standard {@link IntegrationFlow} {@code from()} overloaded methods (for + * convenience), this class introduces {@link #fromSupplier(String)} factory methods to + * resolve the target {@link Supplier} by its name or function definition from the + * provided {@link FunctionCatalog}. *

- * This class represents a DSL for functions composition via integration endpoints. - * Extra processing can be done in between functions by the regular {@link IntegrationFlow} operators: - *

+ * This class represents a DSL for functions composition via integration endpoints. Extra
+ * processing can be done in between functions by the regular {@link IntegrationFlow}
+ * operators: 
  * {@code
- * @Bean
+ * @Bean
  * IntegrationFlow someFunctionFlow(FunctionFlowBuilder functionFlowBuilder) {
  *		return functionFlowBuilder
  *				.fromSupplier("timeSupplier")
@@ -64,7 +65,6 @@
  * 
* * @author Artem Bilan - * * @since 4.0.3 */ public class FunctionFlowBuilder { diff --git a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowDefinition.java b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowDefinition.java index 833866d68..895a76237 100644 --- a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowDefinition.java +++ b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowDefinition.java @@ -28,10 +28,10 @@ /** * The {@link IntegrationFlowExtension} implementation for Spring Cloud Function domain. * Adds operators for functions and consumers and overloaded versions based on their names - * or definitions resolved from the provided {@link org.springframework.cloud.function.context.FunctionCatalog}. + * or definitions resolved from the provided + * {@link org.springframework.cloud.function.context.FunctionCatalog}. * * @author Artem Bilan - * * @since 4.0.3 */ public final class FunctionFlowDefinition extends IntegrationFlowExtension { @@ -51,7 +51,8 @@ void addUpstreamComponents(Map components) { } /** - * Configure a {@link org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper} + * Configure a + * {@link org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper} * as a handler in the endpoint by its definition from the * {@link org.springframework.cloud.function.context.FunctionCatalog}. * @param functionDefinition the function definition in the function catalog. @@ -71,7 +72,8 @@ public FunctionFlowDefinition apply(Function, ?> function) { } /** - * Configure a {@link org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper} + * Configure a + * {@link org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper} * as a one-way handler in the final endpoint by its definition from the * {@link org.springframework.cloud.function.context.FunctionCatalog}. * @param consumerDefinition the consumer definition in the function catalog. @@ -87,8 +89,7 @@ public IntegrationFlow accept(String consumerDefinition) { * @return the current flow builder. */ public IntegrationFlow accept(Consumer> consumer) { - return handle(consumer::accept) - .get(); + return handle(consumer::accept).get(); } } diff --git a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionLookupHelper.java b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionLookupHelper.java index 94b871b29..5791bca19 100644 --- a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionLookupHelper.java +++ b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionLookupHelper.java @@ -26,11 +26,11 @@ import org.springframework.util.Assert; /** - * The helper class to lookup functions from the catalog in lazy manner and cache their instances. + * The helper class to lookup functions from the catalog in lazy manner and cache their + * instances. * * @author Artem Bilan * @author Omer Celik - * * @since 4.0.3 */ public class FunctionLookupHelper { @@ -60,16 +60,17 @@ private Supplier lazyLookup(Class functionType, String functionDefinit return memoize(() -> requireNonNull(functionType, functionDefinition)); } - private T requireNonNull(Class functionType, String functionDefinition) { + private T requireNonNull(Class functionType, String functionDefinition) { T function = this.functionCatalog.lookup(functionType, functionDefinition); Assert.notNull(function, () -> "No '" + functionDefinition + "' in the catalog"); return function; } /** - * The delegate {@link Supplier#get()} is called exactly once and the result is cached. - * @param Generic type of supplied value - * @param delegate The actual Supplier + * The delegate {@link Supplier#get()} is called exactly once and the result is + * cached. + * @param Generic type of supplied value + * @param delegate The actual Supplier * @return The memoized Supplier */ private static Supplier memoize(Supplier delegate) { diff --git a/spring-cloud-function-integration/src/test/java/org/springframework/cloud/function/integration/dsl/FunctionFlowTests.java b/spring-cloud-function-integration/src/test/java/org/springframework/cloud/function/integration/dsl/FunctionFlowTests.java index aa38492c9..5657964a1 100644 --- a/spring-cloud-function-integration/src/test/java/org/springframework/cloud/function/integration/dsl/FunctionFlowTests.java +++ b/spring-cloud-function-integration/src/test/java/org/springframework/cloud/function/integration/dsl/FunctionFlowTests.java @@ -54,7 +54,6 @@ /** * @author Artem Bilan - * * @since 4.0.3 */ @SpringBootTest @@ -77,9 +76,7 @@ void fromSupplierOverFunctionToConsumer(@Autowired SourcePollingChannelAdapter s String result = this.results.poll(10, TimeUnit.SECONDS); assertThat(result).isEqualTo("SIMPLE TEST DATA"); Message receive = wireTapChannel.receive(10_000); - assertThat(receive) - .extracting(Message::getPayload) - .isEqualTo("simple test data".getBytes()); + assertThat(receive).extracting(Message::getPayload).isEqualTo("simple test data".getBytes()); supplierEndpoint.stop(); } @@ -98,34 +95,32 @@ void fromChannelToFunctionComposition(@Autowired MessageChannel functionComposit result = this.results.poll(10, TimeUnit.SECONDS); assertThat(result).isEqualTo("COMPOSE AGAIN"); - // Ensure that FunctionLookupHelper.memoize() does its trick calling FunctionCatalog.lookup() only once + // Ensure that FunctionLookupHelper.memoize() does its trick calling + // FunctionCatalog.lookup() only once verify(this.functionCatalog).lookup(Consumer.class, "upperCaseFunction|simpleStringConsumer"); } @Test void noFunctionInCatalogException(@Autowired IntegrationFlowContext integrationFlowContext) { - // We need to mock here since BeanFactoryAwareFunctionRegistry will have slightly different logic + // We need to mock here since BeanFactoryAwareFunctionRegistry will have slightly + // different logic FunctionCatalog mockFunctionCatalog = mock(FunctionCatalog.class); FunctionFlowBuilder functionFlowBuilder = new FunctionFlowBuilder(mockFunctionCatalog); - IntegrationFlow wrongFlow = - functionFlowBuilder.from("inputChannel") - .accept("nonExistingConsumer"); + IntegrationFlow wrongFlow = functionFlowBuilder.from("inputChannel").accept("nonExistingConsumer"); - IntegrationFlowContext.IntegrationFlowRegistration registration = - integrationFlowContext.registration(wrongFlow) - .register(); + IntegrationFlowContext.IntegrationFlowRegistration registration = integrationFlowContext.registration(wrongFlow) + .register(); assertThatExceptionOfType(MessageDeliveryException.class) - .isThrownBy(() -> registration.getInputChannel().send(new GenericMessage<>("test"))) - .withRootCauseInstanceOf(IllegalArgumentException.class) - .withStackTraceContaining("No 'nonExistingConsumer' in the catalog"); + .isThrownBy(() -> registration.getInputChannel().send(new GenericMessage<>("test"))) + .withRootCauseInstanceOf(IllegalArgumentException.class) + .withStackTraceContaining("No 'nonExistingConsumer' in the catalog"); registration.destroy(); } - @EnableAutoConfiguration @Configuration(proxyBeanMethods = false) static class TestIntegrationConfiguration { @@ -163,18 +158,17 @@ QueueChannel wireTapChannel() { @Bean IntegrationFlow someFunctionFlow(FunctionFlowBuilder functionFlowBuilder) { return functionFlowBuilder - .fromSupplier("simpleByteArraySupplier", e -> e.id("supplierEndpoint").autoStartup(false)) - .wireTap("wireTapChannel") - .apply("upperCaseFunction") - .log(LoggingHandler.Level.WARN, FunctionFlowTests.class.getName()) - .accept("simpleStringConsumer"); + .fromSupplier("simpleByteArraySupplier", e -> e.id("supplierEndpoint").autoStartup(false)) + .wireTap("wireTapChannel") + .apply("upperCaseFunction") + .log(LoggingHandler.Level.WARN, FunctionFlowTests.class.getName()) + .accept("simpleStringConsumer"); } @Bean IntegrationFlow functionCompositionFlow(FunctionFlowBuilder functionFlowBuilder) { - return functionFlowBuilder - .from("functionCompositionInput") - .accept("upperCaseFunction|simpleStringConsumer"); + return functionFlowBuilder.from("functionCompositionInput") + .accept("upperCaseFunction|simpleStringConsumer"); } } diff --git a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinSuspendTests.java b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinSuspendTests.java index 7274f311b..283fa64fb 100644 --- a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinSuspendTests.java +++ b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinSuspendTests.java @@ -47,31 +47,38 @@ public void close() { @Test public void typeDiscoveryTests() { create(new Class[] { KotlinSuspendFlowLambdasConfiguration.class, - ContextFunctionCatalogAutoConfigurationKotlinTests.SimpleConfiguration.class }); + ContextFunctionCatalogAutoConfigurationKotlinTests.SimpleConfiguration.class }); FunctionCatalog functionCatalog = this.context.getBean(FunctionCatalog.class); FunctionInvocationWrapper kotlinFunction = functionCatalog.lookup("kotlinFunction"); assertThat(kotlinFunction.isFunction()).isTrue(); - assertThat(kotlinFunction.getInputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); - assertThat(kotlinFunction.getOutputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); + assertThat(kotlinFunction.getInputType().getTypeName()) + .isEqualTo("reactor.core.publisher.Flux"); + assertThat(kotlinFunction.getOutputType().getTypeName()) + .isEqualTo("reactor.core.publisher.Flux"); FunctionInvocationWrapper kotlinConsumer = functionCatalog.lookup("kotlinConsumer"); assertThat(kotlinConsumer.isConsumer()).isTrue(); - assertThat(kotlinConsumer.getInputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); + assertThat(kotlinConsumer.getInputType().getTypeName()) + .isEqualTo("reactor.core.publisher.Flux"); FunctionInvocationWrapper kotlinSupplier = functionCatalog.lookup("kotlinSupplier"); assertThat(kotlinSupplier.isSupplier()).isTrue(); - assertThat(kotlinSupplier.getOutputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); + assertThat(kotlinSupplier.getOutputType().getTypeName()) + .isEqualTo("reactor.core.publisher.Flux"); FunctionInvocationWrapper kotlinPojoFunction = functionCatalog.lookup("kotlinPojoFunction"); assertThat(kotlinPojoFunction.isFunction()).isTrue(); - assertThat(kotlinPojoFunction.getInputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); - assertThat(kotlinPojoFunction.getOutputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); + assertThat(kotlinPojoFunction.getInputType().getTypeName()) + .isEqualTo("reactor.core.publisher.Flux"); + assertThat(kotlinPojoFunction.getOutputType().getTypeName()) + .isEqualTo("reactor.core.publisher.Flux"); } private void create(Class[] types, String... props) { this.context = (GenericApplicationContext) new SpringApplicationBuilder(types).properties(props).run(); this.catalog = this.context.getBean(FunctionCatalog.class); } + } diff --git a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java index 606454e98..deaafb331 100644 --- a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java +++ b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java @@ -57,12 +57,8 @@ public void close() { @Test public void typeDiscoveryTests() { - create(new Class[] { KotlinLambdasConfiguration.class, - SimpleConfiguration.class, - KotlinComponentFunction.class, - KotlinPostProcessingFunction.class, - ComponentUppercase.class, - ComponentWithUnitReturn.class}); + create(new Class[] { KotlinLambdasConfiguration.class, SimpleConfiguration.class, KotlinComponentFunction.class, + KotlinPostProcessingFunction.class, ComponentUppercase.class, ComponentWithUnitReturn.class }); FunctionCatalog functionCatalog = this.context.getBean(FunctionCatalog.class); @@ -70,8 +66,8 @@ public void typeDiscoveryTests() { FunctionInvocationWrapper kotlinPostProcessingFunction = functionCatalog.lookup("kotlinPostProcessingFunction"); kotlinPostProcessingFunction.apply("bob"); kotlinPostProcessingFunction.postProcess(); - KotlinPostProcessingFunction postProcessingFunction = (KotlinPostProcessingFunction) - ((KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper) kotlinPostProcessingFunction.getTarget()).getKotlinLambdaTarget(); + KotlinPostProcessingFunction postProcessingFunction = (KotlinPostProcessingFunction) ((KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper) kotlinPostProcessingFunction + .getTarget()).getKotlinLambdaTarget(); assertThat(postProcessingFunction.getInvoked()).isTrue(); // End test post-processing logic @@ -88,7 +84,8 @@ public void typeDiscoveryTests() { FunctionInvocationWrapper componentWithUnitReturn = functionCatalog.lookup("componentWithUnitReturn"); assertThat(componentWithUnitReturn.isConsumer()).isTrue(); - assertThat(componentWithUnitReturn.getInputType()).isEqualTo(ResolvableType.forClassWithGenerics(Message.class, String.class).getType()); + assertThat(componentWithUnitReturn.getInputType()) + .isEqualTo(ResolvableType.forClassWithGenerics(Message.class, String.class).getType()); FunctionInvocationWrapper kotlinConsumer = functionCatalog.lookup("kotlinConsumer"); assertThat(kotlinConsumer.isConsumer()).isTrue(); @@ -105,7 +102,8 @@ public void typeDiscoveryTests() { FunctionInvocationWrapper kotlinListPojoFunction = functionCatalog.lookup("kotlinListPojoFunction"); assertThat(kotlinListPojoFunction.isFunction()).isTrue(); - assertThat(kotlinListPojoFunction.getInputType().getTypeName()).isEqualTo("java.util.List"); + assertThat(kotlinListPojoFunction.getInputType().getTypeName()) + .isEqualTo("java.util.List"); assertThat(kotlinListPojoFunction.getOutputType()).isEqualTo(String.class); FunctionInvocationWrapper componentUppercase = functionCatalog.lookup("componentUppercase"); @@ -125,8 +123,7 @@ public void typeDiscoveryTests() { @Test public void testWithComplexTypesAndRouting() { - create(new Class[] { KotlinLambdasConfiguration.class, - SimpleConfiguration.class }); + create(new Class[] { KotlinLambdasConfiguration.class, SimpleConfiguration.class }); FunctionInvocationWrapper function = this.catalog.lookup("kotlinListPojoFunction"); String result = (String) function.apply("[{\"name\":\"Ricky\"}]"); @@ -134,42 +131,42 @@ public void testWithComplexTypesAndRouting() { function = this.catalog.lookup(Function.class, "functionRouter"); result = (String) function.apply(MessageBuilder.withPayload("[{\"name\":\"Ricky\"}]") - .setHeader("spring.cloud.function.definition", "kotlinListPojoFunction").build()); + .setHeader("spring.cloud.function.definition", "kotlinListPojoFunction") + .build()); assertThat(result).isEqualTo("List of: Ricky"); } @Test public void kotlinLambdas() { - create(new Class[] { KotlinLambdasConfiguration.class, - SimpleConfiguration.class }); + create(new Class[] { KotlinLambdasConfiguration.class, SimpleConfiguration.class }); assertThat(this.context.getBean("kotlinFunction")).isInstanceOf(Function1.class); FunctionInvocationWrapper function = this.catalog.lookup(Function.class, "kotlinFunction"); assertThat(function).isInstanceOf(Function.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(function.getInputType()))).isAssignableFrom(String.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(function.getOutputType()))).isAssignableFrom(String.class); - + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(function.getInputType()))) + .isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(function.getOutputType()))) + .isAssignableFrom(String.class); function = this.catalog.lookup(Function.class, "kotlinConsumer"); assertThat(this.context.getBean("kotlinConsumer")).isInstanceOf(Function1.class); assertThat(function).isInstanceOf(Function.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(function.getInputType()))).isAssignableFrom(String.class); - + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(function.getInputType()))) + .isAssignableFrom(String.class); assertThat(this.context.getBean("kotlinSupplier")).isInstanceOf(Function0.class); FunctionInvocationWrapper supplier = this.catalog.lookup(Function.class, "kotlinSupplier"); assertThat(supplier).isInstanceOf(Supplier.class); assertThat(supplier.get()).isEqualTo("Hello"); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(supplier.getOutputType()))).isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(supplier.getOutputType()))) + .isAssignableFrom(String.class); function = this.catalog.lookup(Function.class, "kotlinFunction|function2"); assertThat(function.apply("Hello")).isEqualTo("HELLOfunction2"); - Function javaFunction = this.catalog - .lookup(Function.class, "javaFunction"); - assertThat(javaFunction.apply("Hello")) - .isEqualTo("Hello"); + Function javaFunction = this.catalog.lookup(Function.class, "javaFunction"); + assertThat(javaFunction.apply("Hello")).isEqualTo("Hello"); } private void create(Class[] types, String... props) { diff --git a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/KotlinTypeDiscoveryTests.java b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/KotlinTypeDiscoveryTests.java index b384b17b3..7a9a0ed12 100644 --- a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/KotlinTypeDiscoveryTests.java +++ b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/KotlinTypeDiscoveryTests.java @@ -16,8 +16,6 @@ package org.springframework.cloud.function.kotlin; - - import java.lang.reflect.Type; import org.junit.jupiter.api.Test; @@ -37,4 +35,5 @@ public void testOutputInputTypes() { Type inputType = FunctionTypeUtils.getInputType(functionType); assertThat(FunctionTypeUtils.isMessage(inputType)).isTrue(); } + } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/BasicStringConverter.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/BasicStringConverter.java index 50eed3d7e..1bb7916e0 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/BasicStringConverter.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/BasicStringConverter.java @@ -41,13 +41,13 @@ public BasicStringConverter(ConfigurableListableBeanFactory registry) { public Object convert(Object function, String value) { if (this.conversionService == null && this.registry != null) { ConversionService conversionService = this.registry.getConversionService(); - this.conversionService = conversionService != null ? conversionService - : new DefaultConversionService(); + this.conversionService = conversionService != null ? conversionService : new DefaultConversionService(); } - //Class type = this.inspector.getInputType(function); - Class type = function == null ? Object.class : FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(((FunctionInvocationWrapper) function).getInputType())); - return this.conversionService.canConvert(String.class, type) - ? this.conversionService.convert(value, type) : value; + // Class type = this.inspector.getInputType(function); + Class type = function == null ? Object.class : FunctionTypeUtils + .getRawType(FunctionTypeUtils.getGenericType(((FunctionInvocationWrapper) function).getInputType())); + return this.conversionService.canConvert(String.class, type) ? this.conversionService.convert(value, type) + : value; } } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionHttpProperties.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionHttpProperties.java index 2ef64bd32..509d2cf93 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionHttpProperties.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionHttpProperties.java @@ -23,36 +23,37 @@ import org.springframework.cloud.function.context.FunctionProperties; /** -* -* @author Oleg Zhurakousky -* @since 4.0.4 -* -*/ + * @author Oleg Zhurakousky + * @since 4.0.4 + * + */ @ConfigurationProperties(prefix = FunctionProperties.PREFIX + ".http") public class FunctionHttpProperties { /** - * Function definition mappings for GET method (e.g. 'spring.cloud.function.http.GET=foo;bar|baz') + * Function definition mappings for GET method (e.g. + * 'spring.cloud.function.http.GET=foo;bar|baz') */ public String get; - /** - * Function definition mappings for POST method (e.g. 'spring.cloud.function.http.POST=foo;bar|baz') + * Function definition mappings for POST method (e.g. + * 'spring.cloud.function.http.POST=foo;bar|baz') */ public String post; /** - * Function definition mappings for PUT method (e.g. 'spring.cloud.function.http.PUT=foo;bar|baz') + * Function definition mappings for PUT method (e.g. + * 'spring.cloud.function.http.PUT=foo;bar|baz') */ public String put; /** - * Function definition mappings for DELETE method (e.g. 'spring.cloud.function.http.DELETE=foo;bar|baz') + * Function definition mappings for DELETE method (e.g. + * 'spring.cloud.function.http.DELETE=foo;bar|baz') */ public String delete; - /** * List of headers to be ignored when generating HttpHeaders (request or response). */ @@ -110,4 +111,5 @@ public List getRequestOnlyHeaders() { public void setRequestOnlyHeaders(List requestOnlyHeaders) { this.requestOnlyHeaders = requestOnlyHeaders; } + } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java index a1feeec8d..ca57a83f1 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java @@ -26,26 +26,22 @@ public abstract class WebRequestConstants { /** * Function attribute name. */ - public static final String FUNCTION = WebRequestConstants.class.getName() - + ".function"; + public static final String FUNCTION = WebRequestConstants.class.getName() + ".function"; /** * Consumer attribute name. */ - public static final String CONSUMER = WebRequestConstants.class.getName() - + ".consumer"; + public static final String CONSUMER = WebRequestConstants.class.getName() + ".consumer"; /** * Supplier attribute name. */ - public static final String SUPPLIER = WebRequestConstants.class.getName() - + ".supplier"; + public static final String SUPPLIER = WebRequestConstants.class.getName() + ".supplier"; /** * Argument attribute name. */ - public static final String ARGUMENT = WebRequestConstants.class.getName() - + ".argument"; + public static final String ARGUMENT = WebRequestConstants.class.getName() + ".argument"; /** * Handler attribute name. diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java index fe241df35..aff836e26 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java @@ -62,13 +62,18 @@ public FunctionController(FunctionHttpProperties functionHttpProperties) { @ResponseBody public Mono> form(ServerWebExchange request) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return request.getFormData().doOnSuccess(params -> wrapper.getParams().addAll(params)) - .then(Mono.defer(() -> (Mono>) FunctionWebRequestProcessingHelper - .processRequest(wrapper, wrapper.getParams(), false, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()))); + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + return request.getFormData() + .doOnSuccess(params -> wrapper.getParams().addAll(params)) + .then(Mono + .defer(() -> (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, + wrapper.getParams(), false, functionHttpProperties.getIgnoredHeaders(), + functionHttpProperties.getRequestOnlyHeaders()))); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", + wrapper.getFunction().getFunctionDefinition())); } } @@ -78,60 +83,66 @@ public Mono> form(ServerWebExchange request) { @ResponseBody public Mono> multipart(ServerWebExchange request) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return request.getMultipartData() - .doOnSuccess(params -> wrapper.getParams().addAll(multi(params))) - .then(Mono.defer(() -> (Mono>) FunctionWebRequestProcessingHelper - .processRequest(wrapper, wrapper.getParams(), false, - functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()))); + .doOnSuccess(params -> wrapper.getParams().addAll(multi(params))) + .then(Mono + .defer(() -> (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, + wrapper.getParams(), false, functionHttpProperties.getIgnoredHeaders(), + functionHttpProperties.getRequestOnlyHeaders()))); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", + wrapper.getFunction().getFunctionDefinition())); } } @SuppressWarnings("unchecked") @PostMapping(path = "/**") @ResponseBody - public Mono> post(ServerWebExchange request, - @RequestBody(required = false) String body) { + public Mono> post(ServerWebExchange request, @RequestBody(required = false) String body) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", + wrapper.getFunction().getFunctionDefinition())); } } @SuppressWarnings("unchecked") @PutMapping(path = "/**") @ResponseBody - public Mono> put(ServerWebExchange request, - @RequestBody(required = false) String body) { + public Mono> put(ServerWebExchange request, @RequestBody(required = false) String body) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("PUT", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("PUT", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("PUT", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("PUT", + wrapper.getFunction().getFunctionDefinition())); } } @SuppressWarnings("unchecked") @DeleteMapping(path = "/**") @ResponseBody - public Mono> delete(ServerWebExchange request, - @RequestBody(required = false) String body) { + public Mono> delete(ServerWebExchange request, @RequestBody(required = false) String body) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("DELETE", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("DELETE", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("DELETE", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("DELETE", + wrapper.getFunction().getFunctionDefinition())); } } @@ -139,12 +150,14 @@ public Mono> delete(ServerWebExchange request, @ResponseBody public Publisher postStream(ServerWebExchange request, @RequestBody(required = false) Flux body) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, true, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", + wrapper.getFunction().getFunctionDefinition())); } } @@ -153,12 +166,14 @@ public Publisher postStream(ServerWebExchange request, @RequestBody(required @ResponseBody public Publisher getStream(ServerWebExchange request) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), true, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", + wrapper.getFunction().getFunctionDefinition())); } } @@ -167,18 +182,21 @@ public Publisher getStream(ServerWebExchange request) { @ResponseBody public Mono> get(ServerWebExchange request) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false, - functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + return (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, + wrapper.getArgument(), false, functionHttpProperties.getIgnoredHeaders(), + functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", + wrapper.getFunction().getFunctionDefinition())); } } private FunctionWrapper wrapper(ServerWebExchange request) { FunctionInvocationWrapper function = (FunctionInvocationWrapper) request - .getAttribute(WebRequestConstants.HANDLER); + .getAttribute(WebRequestConstants.HANDLER); HttpHeaders headers = new HttpHeaders(); headers.putAll(request.getRequest().getHeaders()); headers.set("uri", request.getRequest().getURI().toString()); @@ -204,4 +222,5 @@ private MultiValueMap multi(MultiValueMap body) { } return map; } + } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java index de865ff32..94f64c014 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java @@ -16,7 +16,6 @@ package org.springframework.cloud.function.web.flux; - import reactor.core.publisher.Mono; import org.springframework.beans.factory.InitializingBean; @@ -39,8 +38,7 @@ */ @Configuration @ConditionalOnClass(RequestMappingHandlerMapping.class) -public class FunctionHandlerMapping extends RequestMappingHandlerMapping - implements InitializingBean { +public class FunctionHandlerMapping extends RequestMappingHandlerMapping implements InitializingBean { private final FunctionCatalog functions; @@ -52,8 +50,8 @@ public class FunctionHandlerMapping extends RequestMappingHandlerMapping private String prefix = ""; @Autowired - public FunctionHandlerMapping(FunctionCatalog catalog, - FunctionController controller, FunctionProperties functionProperties) { + public FunctionHandlerMapping(FunctionCatalog catalog, FunctionController controller, + FunctionProperties functionProperties) { this.functions = catalog; this.logger.info("FunctionCatalog: " + catalog); setOrder(super.getOrder() - 5); @@ -83,8 +81,8 @@ public Mono getHandlerInternal(ServerWebExchange request) { if (path.startsWith(this.prefix)) { path = path.substring(this.prefix.length()); } - Object function = FunctionWebRequestProcessingHelper - .findFunction(this.functionProperties, request.getRequest().getMethod(), this.functions, request.getAttributes(), path); + Object function = FunctionWebRequestProcessingHelper.findFunction(this.functionProperties, + request.getRequest().getMethod(), this.functions, request.getAttributes(), path); if (function != null) { if (this.logger.isDebugEnabled()) { @@ -99,4 +97,5 @@ public Mono getHandlerInternal(ServerWebExchange request) { @Override protected void initHandlerMethods() { } + } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java index d632e9095..c9f5a58b3 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java @@ -48,7 +48,8 @@ public class ReactorAutoConfiguration { @Bean - public FunctionHandlerMapping functionHandlerMapping(FunctionCatalog catalog, FunctionController controller, FunctionProperties functionProperties) { + public FunctionHandlerMapping functionHandlerMapping(FunctionCatalog catalog, FunctionController controller, + FunctionProperties functionProperties) { return new FunctionHandlerMapping(catalog, controller, functionProperties); } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java index 7ceb559a7..c72f5db89 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java @@ -85,13 +85,14 @@ public class FunctionEndpointInitializer implements ApplicationContextInitializer { private static boolean webflux = ClassUtils - .isPresent("org.springframework.web.reactive.function.server.RouterFunction", null); + .isPresent("org.springframework.web.reactive.function.server.RouterFunction", null); @Override public void initialize(GenericApplicationContext context) { if (webflux && ContextFunctionCatalogInitializer.enabled - && context.getEnvironment().getProperty(FunctionalSpringApplication.SPRING_WEB_APPLICATION_TYPE, - WebApplicationType.class, WebApplicationType.REACTIVE) == WebApplicationType.REACTIVE + && context.getEnvironment() + .getProperty(FunctionalSpringApplication.SPRING_WEB_APPLICATION_TYPE, WebApplicationType.class, + WebApplicationType.REACTIVE) == WebApplicationType.REACTIVE && context.getEnvironment().getProperty("spring.functional.enabled", Boolean.class, false)) { registerEndpoint(context); registerWebFluxAutoConfiguration(context); @@ -108,15 +109,18 @@ private void registerWebFluxAutoConfiguration(GenericApplicationContext context) private void registerEndpoint(GenericApplicationContext context) { context.registerBean(FunctionHttpProperties.class, () -> new FunctionHttpProperties()); context.registerBean(FunctionEndpointFactory.class, - () -> new FunctionEndpointFactory(context.getBean(FunctionProperties.class), context.getBean(FunctionCatalog.class), - context.getEnvironment(), context.getBean(FunctionHttpProperties.class))); + () -> new FunctionEndpointFactory(context.getBean(FunctionProperties.class), + context.getBean(FunctionCatalog.class), context.getEnvironment(), + context.getBean(FunctionHttpProperties.class))); RouterFunctionRegister.register(context); } private HttpWebHandlerAdapter httpHandler(GenericApplicationContext context) { return (HttpWebHandlerAdapter) RouterFunctions.toHttpHandler(context.getBean(RouterFunction.class), - HandlerStrategies.empty().exceptionHandler(context.getBeansOfType(WebExceptionHandler.class).values().iterator().next()) - .codecs(config -> config.registerDefaults(true)).build()); + HandlerStrategies.empty() + .exceptionHandler(context.getBeansOfType(WebExceptionHandler.class).values().iterator().next()) + .codecs(config -> config.registerDefaults(true)) + .build()); } private DefaultErrorWebExceptionHandler errorHandler(GenericApplicationContext context) { @@ -125,8 +129,8 @@ private DefaultErrorWebExceptionHandler errorHandler(GenericApplicationContext c context.registerBean(Resources.class, () -> new Resources()); DefaultErrorWebExceptionHandler handler = new DefaultErrorWebExceptionHandler( - context.getBeansOfType(ErrorAttributes.class).values().iterator().next(), context.getBean(Resources.class), - context.getBean(ErrorProperties.class), context); + context.getBeansOfType(ErrorAttributes.class).values().iterator().next(), + context.getBean(Resources.class), context.getBean(ErrorProperties.class), context); ServerCodecConfigurer codecs = ServerCodecConfigurer.create(); handler.setMessageWriters(codecs.getWriters()); handler.setMessageReaders(codecs.getReaders()); @@ -168,9 +172,8 @@ public void onApplicationEvent(ApplicationEvent event) { HttpHandler handler = context.getBeansOfType(HttpHandler.class).values().iterator().next(); ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler); HttpServer httpServer = HttpServer.create().host(address).port(port).handle(adapter); - Thread thread = new Thread( - () -> httpServer.bindUntilJavaShutdown(Duration.ofSeconds(60), (server) -> callback(server, context)), - "server-startup"); + Thread thread = new Thread(() -> httpServer.bindUntilJavaShutdown(Duration.ofSeconds(60), + (server) -> callback(server, context)), "server-startup"); thread.setDaemon(false); thread.start(); } @@ -179,8 +182,10 @@ public void onApplicationEvent(ApplicationEvent event) { private void callback(DisposableServer server, ApplicationContext context) { logger.info("HTTP server started on port: " + server.port()); if (context instanceof ConfigurableApplicationContext) { - ((ConfigurableApplicationContext) context).getEnvironment().getPropertySources().addFirst( - new MapPropertySource("functionalServerProps", Collections.singletonMap("local.server.port", server.port()))); + ((ConfigurableApplicationContext) context).getEnvironment() + .getPropertySources() + .addFirst(new MapPropertySource("functionalServerProps", + Collections.singletonMap("local.server.port", server.port()))); } try { double uptime = ManagementFactory.getRuntimeMXBean().getUptime(); @@ -212,7 +217,8 @@ class FunctionEndpointFactory { private final FunctionHttpProperties functionHttpProperties; - FunctionEndpointFactory(FunctionProperties functionProperties, FunctionCatalog functionCatalog, Environment environment, FunctionHttpProperties functionHttpProperties) { + FunctionEndpointFactory(FunctionProperties functionProperties, FunctionCatalog functionCatalog, + Environment environment, FunctionHttpProperties functionHttpProperties) { String handler = environment.resolvePlaceholders("${function.handler}"); if (handler.startsWith("$")) { handler = null; @@ -232,8 +238,8 @@ private FunctionInvocationWrapper extract(ServerRequest request) { function = this.functionCatalog.lookup(Function.class, handler); } else { - function = FunctionWebRequestProcessingHelper.findFunction(this.functionProperties, request.method(), functionCatalog, request.attributes(), - request.path()); + function = FunctionWebRequestProcessingHelper.findFunction(this.functionProperties, request.method(), + functionCatalog, request.attributes(), request.path()); } return function; } @@ -246,24 +252,29 @@ public RouterFunction functionEndpoints() { : FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(funcWrapper.getOutputType())); FunctionWrapper wrapper = new FunctionWrapper(funcWrapper, null); Mono> stream = request.bodyToMono(String.class) - .flatMap(content -> (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, content, false, - functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders())); + .flatMap(content -> (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, + content, false, functionHttpProperties.getIgnoredHeaders(), + functionHttpProperties.getRequestOnlyHeaders())); return stream.flatMap(entity -> { - BodyBuilder builder = status(entity.getStatusCode()).headers(headers -> headers.addAll(entity.getHeaders())); + BodyBuilder builder = status(entity.getStatusCode()) + .headers(headers -> headers.addAll(entity.getHeaders())); if (outputType == null) { // consumer return builder.build(); } else { - return builder.body(entity != null && entity.hasBody() ? Mono.just((T) entity.getBody()) : Mono.empty(), outputType); + return builder.body( + entity != null && entity.hasBody() ? Mono.just((T) entity.getBody()) : Mono.empty(), + outputType); } }); }).andRoute(GET("/**"), request -> { FunctionInvocationWrapper funcWrapper = extract(request); Class outputType = FunctionTypeUtils - .getRawType(FunctionTypeUtils.getGenericType(funcWrapper.getOutputType())); + .getRawType(FunctionTypeUtils.getGenericType(funcWrapper.getOutputType())); if (funcWrapper.isSupplier()) { - Object result = FunctionWebRequestProcessingHelper.invokeFunction(funcWrapper, null, funcWrapper.isInputTypeMessage()); + Object result = FunctionWebRequestProcessingHelper.invokeFunction(funcWrapper, null, + funcWrapper.isInputTypeMessage()); if (!(result instanceof Publisher)) { result = Mono.just(result); } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionController.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionController.java index 63880038a..3d62abbb8 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionController.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionController.java @@ -70,19 +70,24 @@ public FunctionController(FunctionHttpProperties functionHttpProperties) { this.functionHttpProperties = functionHttpProperties; } - @PostMapping(path = "/**", consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE, - MediaType.MULTIPART_FORM_DATA_VALUE }) + @PostMapping(path = "/**", + consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE }) @ResponseBody public Object form(WebRequest request) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { if (((ServletWebRequest) request).getRequest() instanceof StandardMultipartHttpServletRequest) { MultiValueMap multiFileMap = ((StandardMultipartHttpServletRequest) ((ServletWebRequest) request) - .getRequest()).getMultiFileMap(); + .getRequest()).getMultiFileMap(); if (!CollectionUtils.isEmpty(multiFileMap)) { - List> files = multiFileMap.values().stream().flatMap(v -> v.stream()) - .map(file -> MessageBuilder.withPayload(file).copyHeaders(wrapper.getHeaders().asMultiValueMap()).build()) - .collect(Collectors.toList()); + List> files = multiFileMap.values() + .stream() + .flatMap(v -> v.stream()) + .map(file -> MessageBuilder.withPayload(file) + .copyHeaders(wrapper.getHeaders().asMultiValueMap()) + .build()) + .collect(Collectors.toList()); FunctionInvocationWrapper function = wrapper.getFunction(); Publisher result = (Publisher) function.apply(Flux.fromIterable(files)); @@ -99,7 +104,8 @@ public Object form(WebRequest request) { functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", + wrapper.getFunction().getFunctionDefinition())); } } @@ -110,13 +116,17 @@ public Mono>> postStream(WebRequest request, @RequestBody(required = false) String body) { String argument = StringUtils.hasText(body) ? body : ""; FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return ((Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, argument, true, - functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders())).map(response -> ResponseEntity.ok() - .headers(response.getHeaders()).body((Publisher) response.getBody())); + functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders())) + .map(response -> ResponseEntity.ok() + .headers(response.getHeaders()) + .body((Publisher) response.getBody())); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", + wrapper.getFunction().getFunctionDefinition())); } } @@ -124,12 +134,14 @@ public Mono>> postStream(WebRequest request, @ResponseBody public Publisher getStream(WebRequest request) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), true, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", + wrapper.getFunction().getFunctionDefinition())); } } @@ -137,13 +149,15 @@ public Publisher getStream(WebRequest request) { @ResponseBody public Object post(WebRequest request, @RequestBody(required = false) String body) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { Assert.isTrue(!wrapper.getFunction().isSupplier(), "'POST' can only be mapped to Function or Consumer"); return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", + wrapper.getFunction().getFunctionDefinition())); } } @@ -151,12 +165,14 @@ public Object post(WebRequest request, @RequestBody(required = false) String bod @ResponseBody public Object put(WebRequest request, @RequestBody(required = false) String body) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("PUT", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("PUT", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("PUT", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("PUT", + wrapper.getFunction().getFunctionDefinition())); } } @@ -164,13 +180,15 @@ public Object put(WebRequest request, @RequestBody(required = false) String body @ResponseStatus(HttpStatus.NO_CONTENT) public void delete(WebRequest request, @RequestBody(required = false) String body) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("DELETE", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("DELETE", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { Assert.isTrue(wrapper.getFunction().isConsumer(), "'DELETE' can only be mapped to Consumer"); FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("DELETE", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("DELETE", + wrapper.getFunction().getFunctionDefinition())); } } @@ -178,19 +196,22 @@ public void delete(WebRequest request, @RequestBody(required = false) String bod @ResponseBody public Object get(WebRequest request) { FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { + if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", + wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false, functionHttpProperties.getIgnoredHeaders(), functionHttpProperties.getRequestOnlyHeaders()); } else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition())); + throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", + wrapper.getFunction().getFunctionDefinition())); } } private FunctionWrapper wrapper(WebRequest request) { FunctionInvocationWrapper function = (FunctionInvocationWrapper) request - .getAttribute(WebRequestConstants.HANDLER, WebRequest.SCOPE_REQUEST); - FunctionWrapper wrapper = new FunctionWrapper(function, (((ServletWebRequest) request).getRequest()).getMethod()); + .getAttribute(WebRequestConstants.HANDLER, WebRequest.SCOPE_REQUEST); + FunctionWrapper wrapper = new FunctionWrapper(function, + (((ServletWebRequest) request).getRequest()).getMethod()); for (String key : request.getParameterMap().keySet()) { wrapper.getParams().addAll(key, Arrays.asList(request.getParameterValues(key))); } @@ -202,11 +223,11 @@ private FunctionWrapper wrapper(WebRequest request) { HttpHeaders headers = HttpHeaders.copyOf(wrapper.getHeaders()); headers.set("uri", ((ServletWebRequest) request).getRequest().getRequestURI()); - String argument = (String) request.getAttribute(WebRequestConstants.ARGUMENT, - WebRequest.SCOPE_REQUEST); + String argument = (String) request.getAttribute(WebRequestConstants.ARGUMENT, WebRequest.SCOPE_REQUEST); if (argument != null) { wrapper.setArgument(argument); } return wrapper; } + } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionHandlerMapping.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionHandlerMapping.java index ed85403f0..fa8a8990e 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionHandlerMapping.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionHandlerMapping.java @@ -35,16 +35,13 @@ import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - - /** * @author Dave Syer * @author Oleg Zhurakousky */ @Configuration @ConditionalOnClass(RequestMappingHandlerMapping.class) -public class FunctionHandlerMapping extends RequestMappingHandlerMapping - implements InitializingBean { +public class FunctionHandlerMapping extends RequestMappingHandlerMapping implements InitializingBean { private final FunctionCatalog functions; @@ -79,14 +76,12 @@ protected void initHandlerMethods() { } @Override - protected HandlerMethod getHandlerInternal(HttpServletRequest request) - throws Exception { + protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { HandlerMethod handler = super.getHandlerInternal(request); if (handler == null) { return null; } - String path = (String) request - .getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); + String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); if (path == null) { return handler; } @@ -97,8 +92,9 @@ protected HandlerMethod getHandlerInternal(HttpServletRequest request) path = path.substring(this.prefix.length()); } - Object function = FunctionWebRequestProcessingHelper.findFunction(this.functionProperties, HttpMethod.valueOf(request.getMethod()), - this.functions, new HttpRequestAttributeDelegate(request), path); + Object function = FunctionWebRequestProcessingHelper.findFunction(this.functionProperties, + HttpMethod.valueOf(request.getMethod()), this.functions, new HttpRequestAttributeDelegate(request), + path); if (function != null) { if (this.logger.isDebugEnabled()) { this.logger.debug("Found function for GET: " + path); @@ -111,7 +107,9 @@ protected HandlerMethod getHandlerInternal(HttpServletRequest request) @SuppressWarnings("serial") private static class HttpRequestAttributeDelegate extends HashMap { + private final HttpServletRequest request; + HttpRequestAttributeDelegate(HttpServletRequest request) { this.request = request; } @@ -121,6 +119,7 @@ public Object put(String key, Object value) { this.request.setAttribute(key, value); return value; } + } } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/ReactorAutoConfiguration.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/ReactorAutoConfiguration.java index 453f9ac0e..c39b3f515 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/ReactorAutoConfiguration.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/ReactorAutoConfiguration.java @@ -40,11 +40,12 @@ @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Flux.class, AsyncHandlerMethodReturnValueHandler.class }) -@Import({ FunctionController.class}) +@Import({ FunctionController.class }) public class ReactorAutoConfiguration { @Bean - public FunctionHandlerMapping functionHandlerMapping(FunctionProperties functionProperties, FunctionCatalog catalog, FunctionController controller) { + public FunctionHandlerMapping functionHandlerMapping(FunctionProperties functionProperties, FunctionCatalog catalog, + FunctionController controller) { return new FunctionHandlerMapping(functionProperties, catalog, controller); } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/ExporterProperties.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/ExporterProperties.java index 48e61cc7e..877451652 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/ExporterProperties.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/ExporterProperties.java @@ -151,7 +151,8 @@ public static class Sink { private String name; /** - * Content type to use when serializing source's output for transport (default 'application/json`). + * Content type to use when serializing source's output for transport (default + * 'application/json`). */ private String contentType = "application/json"; @@ -182,6 +183,7 @@ public String getContentType() { public void setContentType(String contentType) { this.contentType = contentType; } + } } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java index 29911c68e..6203acfc4 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java @@ -45,7 +45,7 @@ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(WebClient.class) @Conditional(SourceActiveCondition.class) -@EnableConfigurationProperties({ExporterProperties.class, FunctionHttpProperties.class}) +@EnableConfigurationProperties({ ExporterProperties.class, FunctionHttpProperties.class }) public class FunctionExporterAutoConfiguration { private final ExporterProperties props; diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterInitializer.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterInitializer.java index 42beb3de8..3de0c91bf 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterInitializer.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterInitializer.java @@ -65,8 +65,8 @@ public Builder get() { } private boolean isExporting(GenericApplicationContext context) { - Boolean enabled = context.getEnvironment().getProperty("spring.cloud.function.web.export.enabled", - Boolean.class); + Boolean enabled = context.getEnvironment() + .getProperty("spring.cloud.function.web.export.enabled", Boolean.class); if (enabled != null) { return enabled; } @@ -84,24 +84,25 @@ private boolean isExporting(GenericApplicationContext context) { private void registerExport(GenericApplicationContext context) { context.registerBean(ExporterProperties.class, () -> new ExporterProperties()); context.registerBean(FunctionExporterAutoConfiguration.class, - () -> new FunctionExporterAutoConfiguration(context.getBean(ExporterProperties.class), context.getBean(FunctionHttpProperties.class))); + () -> new FunctionExporterAutoConfiguration(context.getBean(ExporterProperties.class), + context.getBean(FunctionHttpProperties.class))); if (context.getBeanFactory().getBeanNamesForType(DestinationResolver.class, false, false).length == 0) { context.registerBean(DestinationResolver.class, () -> context.getBean(FunctionExporterAutoConfiguration.class).simpleDestinationResolver()); } if (context.getBeanFactory().getBeanNamesForType(RequestBuilder.class, false, false).length == 0) { context.registerBean(RequestBuilder.class, () -> context.getBean(FunctionExporterAutoConfiguration.class) - .simpleRequestBuilder(context.getEnvironment())); + .simpleRequestBuilder(context.getEnvironment())); } if (context.getEnvironment().getProperty("spring.cloud.function.web.export.source.url") != null) { - context.registerBean("origin", FunctionRegistration.class, () -> context - .getBean(FunctionExporterAutoConfiguration.class).origin(context.getBean(WebClient.Builder.class))); + context.registerBean("origin", FunctionRegistration.class, + () -> context.getBean(FunctionExporterAutoConfiguration.class) + .origin(context.getBean(WebClient.Builder.class))); } if (context.getEnvironment().getProperty("spring.cloud.function.web.export.sink.url") != null) { - context.registerBean(SupplierExporter.class, - () -> context.getBean(FunctionExporterAutoConfiguration.class).sourceForwarder( - context.getBean(RequestBuilder.class), context.getBean(DestinationResolver.class), - context.getBean(FunctionCatalog.class), context.getBean(WebClient.Builder.class))); + context.registerBean(SupplierExporter.class, () -> context.getBean(FunctionExporterAutoConfiguration.class) + .sourceForwarder(context.getBean(RequestBuilder.class), context.getBean(DestinationResolver.class), + context.getBean(FunctionCatalog.class), context.getBean(WebClient.Builder.class))); } } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/HttpSupplier.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/HttpSupplier.java index 6e5376040..64561f487 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/HttpSupplier.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/HttpSupplier.java @@ -33,8 +33,8 @@ /** * A {@link Supplier} that pulls data from an HTTP endpoint. Repeatedly polls the endpoint - * until a non-2xx response is received, at which point it will repeatedly produced a - * Mono at 1 sec intervals until the next 2xx response. + * until a non-2xx response is received, at which point it will repeatedly produced a Mono + * at 1 sec intervals until the next 2xx response. * * @author Dave Syer * @author Oleg Zhurakousky @@ -80,8 +80,7 @@ private Mono transform(ClientResponse response) { } return Mono.delay(Duration.ofSeconds(1)); } - return response.bodyToMono(this.props.getSource().getType()) - .map(value -> message(response, value)); + return response.bodyToMono(this.props.getSource().getType()).map(value -> message(response, value)); } private Object message(ClientResponse response, Object payload) { @@ -89,11 +88,11 @@ private Object message(ClientResponse response, Object payload) { return payload; } return MessageBuilder.withPayload(payload) - .copyHeaders(HeaderUtils.fromHttp( - HeaderUtils.sanitize(response.headers().asHttpHeaders(), this.httpProperties.getIgnoredHeaders(), this.httpProperties.getRequestOnlyHeaders()))) - .setHeader("scf-sink-url", this.props.getSink().getUrl()) - .setHeader("scf-func-name", this.props.getSink().getName()) - .build(); + .copyHeaders(HeaderUtils.fromHttp(HeaderUtils.sanitize(response.headers().asHttpHeaders(), + this.httpProperties.getIgnoredHeaders(), this.httpProperties.getRequestOnlyHeaders()))) + .setHeader("scf-sink-url", this.props.getSink().getUrl()) + .setHeader("scf-func-name", this.props.getSink().getName()) + .build(); } @SuppressWarnings("serial") @@ -110,4 +109,5 @@ public synchronized Throwable fillInStackTrace() { } } + } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleRequestBuilder.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleRequestBuilder.java index 8153940ed..58ae336c0 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleRequestBuilder.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleRequestBuilder.java @@ -69,8 +69,7 @@ public HttpHeaders headers(String destination, Object value) { @Override public URI uri(String destination) { try { - return new URI(this.baseUrl.replace("${destination}", destination) - .replace("{{destination}}", destination)); + return new URI(this.baseUrl.replace("${destination}", destination).replace("{{destination}}", destination)); } catch (URISyntaxException e) { throw new IllegalStateException("Cannot create URI", e); diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SupplierExporter.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SupplierExporter.java index b0feaaa2a..387f41318 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SupplierExporter.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SupplierExporter.java @@ -71,8 +71,7 @@ public class SupplierExporter implements SmartLifecycle { private volatile Disposable subscription; - SupplierExporter(RequestBuilder requestBuilder, - DestinationResolver destinationResolver, FunctionCatalog catalog, + SupplierExporter(RequestBuilder requestBuilder, DestinationResolver destinationResolver, FunctionCatalog catalog, WebClient client, ExporterProperties exporterProperties) { this.requestBuilder = requestBuilder; this.destinationResolver = destinationResolver; @@ -106,29 +105,31 @@ public void start() { suppliersPresent = true; } if (suppliersPresent) { - this.subscription = streams - .retryWhen(Retry.backoff(5, Duration.ofSeconds(1))) -// .retry(error -> { -// /* -// * The ConnectException may happen if a server is not yet available/reachable -// * The ClassCast is to handle delayed Mono issued by HttpSupplier.transform for non-2xx responses -// */ -// boolean retry = error instanceof ConnectException || error instanceof ClassCastException -// && this.running; -// if (!retry) { -// this.ok = false; -// if (!this.debug) { -// logger.info(error); -// } -// stop(); -// } -// return retry; -// } -// ) - .doOnComplete(() -> { - stop(); - }) - .subscribe(); + this.subscription = streams.retryWhen(Retry.backoff(5, Duration.ofSeconds(1))) + // .retry(error -> { + // /* + // * The ConnectException may happen if a server is not yet + // available/reachable + // * The ClassCast is to handle delayed Mono issued by + // HttpSupplier.transform for non-2xx responses + // */ + // boolean retry = error instanceof ConnectException || error instanceof + // ClassCastException + // && this.running; + // if (!retry) { + // this.ok = false; + // if (!this.debug) { + // logger.info(error); + // } + // stop(); + // } + // return retry; + // } + // ) + .doOnComplete(() -> { + stop(); + }) + .subscribe(); this.ok = true; this.running = true; @@ -169,9 +170,9 @@ public void stop(Runnable callback) { private Flux forward(Supplier> supplier, String name) { Flux o = (Flux) supplier.get(); -// o.subscribe(v -> { -// System.out.println(v); -// }); + // o.subscribe(v -> { + // System.out.println(v); + // }); return Flux.from(o).flatMap(value -> { String destination = this.destinationResolver.destination(supplier, name, value); if (this.debug) { @@ -190,14 +191,16 @@ private Mono post(URI uri, String destination, Object value) { if (this.debug) { logger.debug("Sending BODY as type: " + body.getClass().getName()); } - Mono result = this.client.post().uri(uri) - .headers(headers -> headers(headers, destination, value)).bodyValue(body) - .exchangeToMono(Mono::just) - .doOnNext(response -> { - if (this.debug) { - logger.debug("Response STATUS: " + response.statusCode()); - } - }); + Mono result = this.client.post() + .uri(uri) + .headers(headers -> headers(headers, destination, value)) + .bodyValue(body) + .exchangeToMono(Mono::just) + .doOnNext(response -> { + if (this.debug) { + logger.debug("Response STATUS: " + response.statusCode()); + } + }); if (this.debug) { result = result.log(); } @@ -211,4 +214,5 @@ private void headers(HttpHeaders headers, String destination, Object value) { private URI uri(String destination) { return this.requestBuilder.uri(destination); } + } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWebRequestProcessingHelper.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWebRequestProcessingHelper.java index 533c589b0..3fb12551e 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWebRequestProcessingHelper.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWebRequestProcessingHelper.java @@ -57,9 +57,10 @@ private FunctionWebRequestProcessingHelper() { } - public static FunctionInvocationWrapper findFunction(FunctionProperties functionProperties, HttpMethod method, FunctionCatalog functionCatalog, - Map attributes, String path) { - if (method.equals(HttpMethod.GET) || method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.DELETE)) { + public static FunctionInvocationWrapper findFunction(FunctionProperties functionProperties, HttpMethod method, + FunctionCatalog functionCatalog, Map attributes, String path) { + if (method.equals(HttpMethod.GET) || method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) + || method.equals(HttpMethod.DELETE)) { return doFindFunction(functionProperties.getDefinition(), method, functionCatalog, attributes, path); } else { @@ -72,7 +73,8 @@ public static Object invokeFunction(FunctionInvocationWrapper function, Object i return postProcessResult(result, isMessage); } - public static boolean isFunctionValidForMethod(String httpMethod, String functionDefinition, FunctionHttpProperties functionHttpProperties) { + public static boolean isFunctionValidForMethod(String httpMethod, String functionDefinition, + FunctionHttpProperties functionHttpProperties) { String functionDefinitions = null; switch (httpMethod) { case "GET": @@ -97,15 +99,17 @@ public static boolean isFunctionValidForMethod(String httpMethod, String functio } public static String buildBadMappingErrorMessage(String httpMethod, String functionDefinition) { - return "Function '" + functionDefinition + "' is not eligible to be invoked " - + "via " + httpMethod + " method. This is due to the fact that explicit mappings for " + httpMethod + return "Function '" + functionDefinition + "' is not eligible to be invoked " + "via " + httpMethod + + " method. This is due to the fact that explicit mappings for " + httpMethod + " are provided via 'spring.cloud.function.http." + httpMethod + "' property " - + "and this function is not listed there. Either remove all explicit mappings for " + httpMethod + " or add this function to the list of functions " - + "specified in 'spring.cloud.function.http." + httpMethod + "' property."; + + "and this function is not listed there. Either remove all explicit mappings for " + httpMethod + + " or add this function to the list of functions " + "specified in 'spring.cloud.function.http." + + httpMethod + "' property."; } @SuppressWarnings({ "rawtypes", "unchecked" }) - public static Publisher processRequest(FunctionWrapper wrapper, Object argument, boolean eventStream, List ignoredHeaders, List requestOnlyHeaders) { + public static Publisher processRequest(FunctionWrapper wrapper, Object argument, boolean eventStream, + List ignoredHeaders, List requestOnlyHeaders) { if (argument == null) { argument = ""; } @@ -119,7 +123,6 @@ public static Publisher processRequest(FunctionWrapper wrapper, Object argume Message inputMessage = null; - MessageBuilder builder = MessageBuilder.withPayload(argument); if (!CollectionUtils.isEmpty(wrapper.getParams())) { builder = builder.setHeader(HeaderUtils.HTTP_REQUEST_PARAM, wrapper.getParams().toSingleValueMap()); @@ -135,11 +138,14 @@ public static Publisher processRequest(FunctionWrapper wrapper, Object argume if (result instanceof Publisher && !function.isComposed()) { Mono.from((Publisher) result).subscribe(); } - return "DELETE".equals(wrapper.getMethod()) ? - Mono.empty() : Mono.just(ResponseEntity.accepted().headers(HeaderUtils.sanitize(headers, ignoredHeaders, requestOnlyHeaders)).build()); + return "DELETE".equals(wrapper.getMethod()) ? Mono.empty() + : Mono.just(ResponseEntity.accepted() + .headers(HeaderUtils.sanitize(headers, ignoredHeaders, requestOnlyHeaders)) + .build()); } - BodyBuilder responseOkBuilder = ResponseEntity.ok().headers(HeaderUtils.sanitize(headers, ignoredHeaders, requestOnlyHeaders)); + BodyBuilder responseOkBuilder = ResponseEntity.ok() + .headers(HeaderUtils.sanitize(headers, ignoredHeaders, requestOnlyHeaders)); Publisher pResult; if (result instanceof Publisher) { @@ -175,13 +181,14 @@ else if (v instanceof Message) { }); } - private static Object processMessage(BodyBuilder responseOkBuilder, Message message, List ignoredHeaders) { + private static Object processMessage(BodyBuilder responseOkBuilder, Message message, + List ignoredHeaders) { responseOkBuilder.headers(HeaderUtils.fromMessage(message.getHeaders(), ignoredHeaders)); return message.getPayload(); } - private static FunctionInvocationWrapper doFindFunction(String functionDefinition, HttpMethod method, FunctionCatalog functionCatalog, - Map attributes, String path) { + private static FunctionInvocationWrapper doFindFunction(String functionDefinition, HttpMethod method, + FunctionCatalog functionCatalog, Map attributes, String path) { path = path.startsWith("/") ? path.substring(1) : path; if (method.equals(HttpMethod.GET)) { @@ -201,8 +208,7 @@ private static FunctionInvocationWrapper doFindFunction(String functionDefinitio } builder.append(element); name = builder.toString(); - value = path.length() > name.length() ? path.substring(name.length() + 1) - : null; + value = path.length() > name.length() ? path.substring(name.length() + 1) : null; FunctionInvocationWrapper function = functionCatalog.lookup(name); if (function != null) { return postProcessFunction(function, value, attributes); @@ -218,7 +224,8 @@ private static FunctionInvocationWrapper doFindFunction(String functionDefinitio return null; } - private static FunctionInvocationWrapper postProcessFunction(FunctionInvocationWrapper function, String argument, Map attributes) { + private static FunctionInvocationWrapper postProcessFunction(FunctionInvocationWrapper function, String argument, + Map attributes) { attributes.put(WebRequestConstants.FUNCTION, function); if (argument != null) { attributes.put(WebRequestConstants.ARGUMENT, argument); @@ -236,8 +243,10 @@ else if (result instanceof Mono) { } else if (result instanceof Message messageResult) { if (messageResult.getPayload() instanceof byte[]) { - //String str = new String((byte[]) messageResult.getPayload()); - result = MessageBuilder.withPayload(messageResult.getPayload()).copyHeaders(((Message) result).getHeaders()).build(); + // String str = new String((byte[]) messageResult.getPayload()); + result = MessageBuilder.withPayload(messageResult.getPayload()) + .copyHeaders(((Message) result).getHeaders()) + .build(); } } else if (result instanceof byte[]) { diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWrapper.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWrapper.java index 122d27641..65fd13100 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWrapper.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWrapper.java @@ -24,11 +24,11 @@ /** * For internal use only. * - * * @author Oleg Zhurakousky * */ public class FunctionWrapper { + private final FunctionInvocationWrapper function; private final MultiValueMap params = new LinkedMultiValueMap<>(); @@ -71,4 +71,5 @@ public MultiValueMap getParams() { public String getMethod() { return method; } + } 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 6391ee7cb..74ee0787d 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 @@ -77,13 +77,14 @@ public static HttpHeaders fromMessage(MessageHeaders headers) { return fromMessage(headers, Collections.EMPTY_LIST); } - - public static HttpHeaders sanitize(HttpHeaders request, List ignoredHeders, List requestOnlyHeaders) { + public static HttpHeaders sanitize(HttpHeaders request, List ignoredHeders, + List requestOnlyHeaders) { HttpHeaders result = new HttpHeaders(); for (String name : request.headerNames()) { List value = request.get(name); name = name.toLowerCase(Locale.ROOT); - if (!IGNORED.containsHeader(name) && !REQUEST_ONLY.containsHeader(name) && !ignoredHeders.contains(name) && !requestOnlyHeaders.contains(name)) { + if (!IGNORED.containsHeader(name) && !REQUEST_ONLY.containsHeader(name) && !ignoredHeders.contains(name) + && !requestOnlyHeaders.contains(name)) { result.put(name, value); } } @@ -100,8 +101,7 @@ public static MessageHeaders fromHttp(HttpHeaders headers) { for (String name : headers.headerNames()) { Collection values = multi(headers.get(name)); name = name.toLowerCase(Locale.ROOT); - Object value = values == null ? null - : (values.size() == 1 ? values.iterator().next() : values); + Object value = values == null ? null : (values.size() == 1 ? values.iterator().next() : values); if (name.toLowerCase(Locale.ROOT).equals(HttpHeaders.CONTENT_TYPE.toLowerCase(Locale.ROOT))) { name = MessageHeaders.CONTENT_TYPE; } 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 5bdb4838e..935542991 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 @@ -63,7 +63,8 @@ * */ // @checkstyle:off -@SpringBootTest(classes = TestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive") +@SpringBootTest(classes = TestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT, + properties = "spring.main.web-application-type=reactive") // @checkstyle:on @AutoConfigureTestRestTemplate public class FluxRestApplicationTests { @@ -86,49 +87,43 @@ public void init() { @Test public void wordsSSE() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), - String.class).getBody()).isEqualTo(sse("foo", "bar")); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), String.class) + .getBody()).isEqualTo(sse("foo", "bar")); } @Test public void wordsJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/words")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\",\"bar\"]"); + .exchange(RequestEntity.get(new URI("/words")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test @Disabled("Fix error handling") public void errorJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/bang")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); + .exchange(RequestEntity.get(new URI("/bang")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\"]"); } @Test public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/words")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test public void foos() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/foos")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/foos")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); } @Test public void getMore() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/get/more")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/get/more")).build(), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @@ -136,8 +131,8 @@ public void getMore() throws Exception { @Test @Disabled("Should this even work? Or do we need to be explicit about the JSON?") public void updates() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/updates")).body("one\ntwo"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/updates")).body("one\ntwo"), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isEqualTo("onetwo"); @@ -145,9 +140,9 @@ public void updates() throws Exception { @Test public void updatesJson() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/updates")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"one\",\"two\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isEqualTo("[\"one\",\"two\"]"); @@ -155,138 +150,130 @@ public void updatesJson() throws Exception { @Test public void addFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/addFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); } @Test public void timeout() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/timeout")).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/timeout")).build(), String.class).getBody()) + .isEqualTo("[\"foo\"]"); } - //@Test + // @Test public void emptyJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/empty")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[]"); + .exchange(RequestEntity.get(new URI("/empty")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[]"); } @Test public void sentences() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/sentences")).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/sentences")).build(), String.class).getBody()) + .isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); } @Test public void sentencesAcceptAny() throws Exception { assertThat( - this.rest - .exchange(RequestEntity.get(new URI("/sentences")) - .accept(MediaType.ALL).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); + this.rest.exchange(RequestEntity.get(new URI("/sentences")).accept(MediaType.ALL).build(), String.class) + .getBody()) + .isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); } @Test public void sentencesAcceptJson() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.get(new URI("/sentences")) - .accept(MediaType.APPLICATION_JSON).build(), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.get(new URI("/sentences")).accept(MediaType.APPLICATION_JSON).build(), String.class); assertThat(result.getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - assertThat(result.getHeaders().getContentType()) - .isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); + assertThat(result.getHeaders().getContentType()).isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); } @Test public void uppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/uppercase")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); } @Test public void uppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/upFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test public void transform() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/transform")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/transform")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); } @Test public void postMore() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/post/more")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/post/more")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); } @Test public void uppercaseGet() throws Exception { - assertThat(this.rest.exchange(RequestEntity.get(new URI("/uppercase/foo")) - .accept(MediaType.TEXT_PLAIN).build(), String.class).getBody()) - .isEqualTo("[FOO]"); + assertThat(this.rest + .exchange(RequestEntity.get(new URI("/uppercase/foo")).accept(MediaType.TEXT_PLAIN).build(), String.class) + .getBody()).isEqualTo("[FOO]"); } @Test public void convertGet() throws Exception { - assertThat(this.rest.exchange(RequestEntity.get(new URI("/wrap/123")) - .accept(MediaType.TEXT_PLAIN).build(), String.class).getBody()) - .isEqualTo("..123.."); + assertThat(this.rest + .exchange(RequestEntity.get(new URI("/wrap/123")).accept(MediaType.TEXT_PLAIN).build(), String.class) + .getBody()).isEqualTo("..123.."); } @Test public void convertGetJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/entity/321")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("{\"value\":321}"); + .exchange(RequestEntity.get(new URI("/entity/321")).accept(MediaType.APPLICATION_JSON).build(), + String.class) + .getBody()).isEqualTo("{\"value\":321}"); } @Test public void uppercaseJsonStream() throws Exception { - assertThat( - this.rest.exchange( - RequestEntity.post(new URI("/maps")) - .contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), - String.class).getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/maps")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class) + .getBody()).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test public void uppercaseSSE() throws Exception { - assertThat(this.rest.exchange(RequestEntity.post(new URI("/uppercase")) - .accept(EVENT_STREAM).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("[FOO]", "[BAR]")); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/uppercase")) + .accept(EVENT_STREAM) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class) + .getBody()).isEqualTo(sse("[FOO]", "[BAR]")); } @Test public void altSSE() throws Exception { - assertThat(this.rest.exchange(RequestEntity.post(new URI("/alt")) - .accept(EVENT_STREAM).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("[FOO]", "[BAR]")); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/alt")) + .accept(EVENT_STREAM) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class) + .getBody()).isEqualTo(sse("[FOO]", "[BAR]")); } private String sse(String... values) { @@ -302,45 +289,45 @@ 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(Locale.ROOT) + "]"); + return Flux.fromIterable(flux).log().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(Locale.ROOT) + "]"); - return Flux.from(result).log() - .then(Mono.fromSupplier(() -> ResponseEntity.ok(result))); + .map(value -> "[" + value.trim().toUpperCase(Locale.ROOT) + "]"); + return Flux.from(result).log().then(Mono.fromSupplier(() -> ResponseEntity.ok(result))); } @PostMapping("/upFoos") public Flux upFoos(@RequestBody List list) { - return Flux.fromIterable(list).log() - .map(value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT))); + return Flux.fromIterable(list) + .log() + .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(Locale.ROOT) + "]") - .flatMap(body -> Mono.just(ResponseEntity.ok(body))); + return Mono.just(id) + .map(value -> "[" + value.trim().toUpperCase(Locale.ROOT) + "]") + .flatMap(body -> Mono.just(ResponseEntity.ok(body))); } @GetMapping("/wrap/{id}") public Mono> wrapGet(@PathVariable int id) { - return Mono.just(id).log().map(value -> ".." + value + "..") - .flatMap(body -> Mono.just(ResponseEntity.ok(body))); + return Mono.just(id) + .log() + .map(value -> ".." + value + "..") + .flatMap(body -> Mono.just(ResponseEntity.ok(body))); } @GetMapping("/entity/{id}") public Mono> entity(@PathVariable Integer id) { - return Mono.just(id).log() - .map(value -> Collections.singletonMap("value", value)); + return Mono.just(id).log().map(value -> Collections.singletonMap("value", value)); } @PostMapping("/maps") - public Flux> maps( - @RequestBody List> flux) { + public Flux> maps(@RequestBody List> flux) { return Flux.fromIterable(flux).map(value -> { 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 0621a5152..77b6807ee 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 @@ -62,7 +62,8 @@ * */ // @checkstyle:off -@SpringBootTest(classes = TestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=servlet") +@SpringBootTest(classes = TestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT, + properties = "spring.main.web-application-type=servlet") // @checkstyle:on @AutoConfigureTestRestTemplate public class MvcRestApplicationTests { @@ -85,49 +86,43 @@ public void init() { @Test public void wordsSSE() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), - String.class).getBody()).isEqualTo(sse("foo", "bar")); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), String.class) + .getBody()).isEqualTo(sse("foo", "bar")); } @Test public void wordsJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/words")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\",\"bar\"]"); + .exchange(RequestEntity.get(new URI("/words")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test @Disabled("Fix error handling") public void errorJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/bang")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); + .exchange(RequestEntity.get(new URI("/bang")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\"]"); } @Test public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/words")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test public void foos() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/foos")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/foos")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); } @Test public void getMore() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/get/more")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/get/more")).build(), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @@ -135,8 +130,8 @@ public void getMore() throws Exception { @Test @Disabled("Should this even work? Or do we need to be explicit about the JSON?") public void updates() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/updates")).body("one\ntwo"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/updates")).body("one\ntwo"), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isEqualTo("onetwo"); @@ -144,9 +139,9 @@ public void updates() throws Exception { @Test public void updatesJson() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/updates")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"one\",\"two\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isEqualTo("[\"one\",\"two\"]"); @@ -154,128 +149,116 @@ public void updatesJson() throws Exception { @Test public void addFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/addFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); } @Test public void timeout() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/timeout")).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/timeout")).build(), String.class).getBody()) + .isEqualTo("[\"foo\"]"); } @Test public void emptyJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/empty")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[]"); + .exchange(RequestEntity.get(new URI("/empty")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[]"); } @Test public void sentences() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/sentences")).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/sentences")).build(), String.class).getBody()) + .isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); } @Test public void sentencesAcceptAny() throws Exception { assertThat( - this.rest - .exchange(RequestEntity.get(new URI("/sentences")) - .accept(MediaType.ALL).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); + this.rest.exchange(RequestEntity.get(new URI("/sentences")).accept(MediaType.ALL).build(), String.class) + .getBody()) + .isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); } @Test public void sentencesAcceptJson() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.get(new URI("/sentences")) - .accept(MediaType.APPLICATION_JSON).build(), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.get(new URI("/sentences")).accept(MediaType.APPLICATION_JSON).build(), String.class); assertThat(result.getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - assertThat(result.getHeaders().getContentType()) - .isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); + assertThat(result.getHeaders().getContentType()).isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); } @Test public void uppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/uppercase")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); } @Test public void uppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/upFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test public void transform() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/transform")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/transform")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); } @Test public void postMore() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/post/more")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/post/more")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); } @Test public void uppercaseGet() { - assertThat(this.rest.getForObject("/uppercase/foo", String.class)) - .isEqualTo("[FOO]"); + assertThat(this.rest.getForObject("/uppercase/foo", String.class)).isEqualTo("[FOO]"); } @Test public void convertGet() { - assertThat(this.rest.getForObject("/wrap/123", String.class)) - .isEqualTo("..123.."); + assertThat(this.rest.getForObject("/wrap/123", String.class)).isEqualTo("..123.."); } @Test public void convertGetJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/entity/321")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("{\"value\":321}"); + .exchange(RequestEntity.get(new URI("/entity/321")).accept(MediaType.APPLICATION_JSON).build(), + String.class) + .getBody()).isEqualTo("{\"value\":321}"); } @Test public void uppercaseJsonStream() throws Exception { - assertThat( - this.rest.exchange( - RequestEntity.post(new URI("/maps")) - .contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), - String.class).getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/maps")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class) + .getBody()).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test public void uppercaseSSE() throws Exception { - assertThat(this.rest.exchange(RequestEntity.post(new URI("/uppercase")) - .accept(EVENT_STREAM).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("[FOO]", "[BAR]")); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/uppercase")) + .accept(EVENT_STREAM) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class) + .getBody()).isEqualTo(sse("[FOO]", "[BAR]")); } private String sse(String... values) { @@ -291,14 +274,14 @@ 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(Locale.ROOT) + "]"); + return Flux.fromIterable(flux).log().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(Locale.ROOT))); + return Flux.fromIterable(list) + .log() + .map(value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT))); } @GetMapping("/uppercase/{id}") @@ -313,13 +296,11 @@ public Mono wrapGet(@PathVariable int id) { @GetMapping("/entity/{id}") public Mono> entity(@PathVariable Integer id) { - return Mono.just(id).log() - .map(value -> Collections.singletonMap("value", value)); + return Mono.just(id).log().map(value -> Collections.singletonMap("value", value)); } @PostMapping("/maps") - public Flux> maps( - @RequestBody List> flux) { + public Flux> maps(@RequestBody List> flux) { return Flux.fromIterable(flux).map(value -> { 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 127ed004e..91d592b0e 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 @@ -35,8 +35,7 @@ * @author Dave Syer * */ -@SpringBootTest({ "spring.main.web-application-type=REACTIVE", - "spring.functional.enabled=false" }) +@SpringBootTest({ "spring.main.web-application-type=REACTIVE", "spring.functional.enabled=false" }) @AutoConfigureWebTestClient(timeout = "10000") @DirtiesContext public class ExplicitNonFunctionalTests { @@ -46,9 +45,14 @@ public class ExplicitNonFunctionalTests { @Test public void words() throws Exception { - this.client - .post().uri("/").body(Mono.just("foo"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("FOO"); + this.client.post() + .uri("/") + .body(Mono.just("foo"), String.class) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("FOO"); } @SpringBootConfiguration 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 b04b5f7b0..b1ef0a238 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 @@ -51,13 +51,13 @@ * @author Dave Syer * @author Oleg Zhurakousky */ -@FunctionalSpringBootTest(classes = ApplicationConfiguration.class, webEnvironment = WebEnvironment.NONE, properties = { - "spring.main.web-application-type=none", - "spring.cloud.function.web.export.sink.url=http://localhost:${my.port}", - "spring.cloud.function.web.export.source.url=http://localhost:${my.port}", - "spring.cloud.function.web.export.sink.name=origin|uppercase", - "spring.cloud.function.web.export.sink.contentType=text/plain", - "spring.cloud.function.web.export.debug=true" }) +@FunctionalSpringBootTest(classes = ApplicationConfiguration.class, webEnvironment = WebEnvironment.NONE, + properties = { "spring.main.web-application-type=none", + "spring.cloud.function.web.export.sink.url=http://localhost:${my.port}", + "spring.cloud.function.web.export.source.url=http://localhost:${my.port}", + "spring.cloud.function.web.export.sink.name=origin|uppercase", + "spring.cloud.function.web.export.sink.contentType=text/plain", + "spring.cloud.function.web.export.debug=true" }) @Disabled public class FunctionalExporterTests { @@ -76,8 +76,7 @@ public static void init() throws Exception { String port = "" + TestSocketUtils.findAvailableTcpPort(); System.setProperty("server.port", port); System.setProperty("my.port", port); - context = SpringApplication.run(RestPojoConfiguration.class, - "--spring.main.web-application-type=reactive"); + context = SpringApplication.run(RestPojoConfiguration.class, "--spring.main.web-application-type=reactive"); app = context.getBean(RestPojoConfiguration.class); // Sometimes the server doesn't start quick enough Thread.sleep(500L); @@ -113,15 +112,15 @@ Function, Message> uppercase() { return value -> { headers.putAll(value.getHeaders()); return MessageBuilder.withPayload(value.getPayload().getName().toUpperCase(Locale.ROOT)) - .copyHeaders(value.getHeaders()).build(); + .copyHeaders(value.getHeaders()) + .build(); }; } @Override public void initialize(GenericApplicationContext context) { - context.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(uppercase()) - .type(FunctionTypeUtils.discoverFunctionTypeFromFunctionFactoryMethod(this.getClass(), "uppercase"))); + context.registerBean("uppercase", FunctionRegistration.class, () -> new FunctionRegistration<>(uppercase()) + .type(FunctionTypeUtils.discoverFunctionTypeFromFunctionFactoryMethod(this.getClass(), "uppercase"))); } public static Type discoverFunctionTypeFromFunctionFactoryMethod(Class clazz, String methodName) { @@ -131,11 +130,13 @@ public static Type discoverFunctionTypeFromFunctionFactoryMethod(Class clazz, public static Type discoverFunctionTypeFromFunctionFactoryMethod(Method method) { return method.getGenericReturnType(); } + } } class Person { + private String name; public String getName() { @@ -145,4 +146,5 @@ public String getName() { public void setName(String name) { this.name = name; } + } 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 e985e496e..92630c01d 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 @@ -42,8 +42,14 @@ public class FunctionalTests { @Test public void words() throws Exception { - this.client.post().uri("/").body(Mono.just("foo"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("FOO"); + this.client.post() + .uri("/") + .body(Mono.just("foo"), String.class) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("FOO"); } @SpringBootConfiguration 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 0a8bd1cfc..ccbfb9318 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 @@ -43,11 +43,14 @@ public class FunctionalWithInputListTests { @Test public void words() throws Exception { - this.client.post().uri("/") - .body(Mono.just("[{\"value\":\"foo\"}, {\"value\":\"bar\"}]"), - String.class) - .exchange().expectStatus().isOk().expectBody(String.class) - .isEqualTo("{\"value\":\"FOOBAR\"}"); + this.client.post() + .uri("/") + .body(Mono.just("[{\"value\":\"foo\"}, {\"value\":\"bar\"}]"), String.class) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("{\"value\":\"FOOBAR\"}"); } @SpringBootConfiguration @@ -55,8 +58,8 @@ protected static class TestConfiguration implements Function, Foo> { @Override public Foo apply(List value) { - return new Foo(value.stream().map(foo -> foo.getValue().toUpperCase(Locale.ROOT)) - .collect(Collectors.joining())); + 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 97f9d5ccc..989d7bc99 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 @@ -47,12 +47,15 @@ public class FunctionalWithInputSetTests { @Test public void words() throws Exception { this.client = this.client.mutate().responseTimeout(Duration.ofSeconds(300)).build(); - String reply = this.client - .post().uri("/") - .body(Mono.just("[{\"value\":\"foo\"}, {\"value\":\"bar\"}]"), String.class) - .exchange() - .expectStatus().isOk().expectBody(String.class).returnResult() - .getResponseBody(); + String reply = this.client.post() + .uri("/") + .body(Mono.just("[{\"value\":\"foo\"}, {\"value\":\"bar\"}]"), String.class) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .returnResult() + .getResponseBody(); assertThat(reply.contains("FOO")).isTrue(); assertThat(reply.contains("BAR")).isTrue(); assertThat(reply.contains("{\"value\":\"")).isTrue(); @@ -63,8 +66,8 @@ protected static class TestConfiguration implements Function, Foo> { @Override public Foo apply(Set value) { - return new Foo(value.stream().map(foo -> foo.getValue().toUpperCase(Locale.ROOT)) - .collect(Collectors.joining())); + 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 a6f14a5e3..ca3672773 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 @@ -30,7 +30,6 @@ import org.springframework.messaging.support.MessageBuilder; import org.springframework.test.web.reactive.server.WebTestClient; - /** * @author Oleg Zhurakousky * @@ -45,22 +44,29 @@ public class HeadersToMessageTests { @Test public void testBodyAndCustomHeaderFromMessagePropagation() throws Exception { - this.client.post().uri("/").body(Mono.just("foo"), String.class).exchange() - .expectStatus().is2xxSuccessful().expectHeader() - .valueEquals("x-content-type", "application/xml").expectHeader() - .valueEquals("foo", "bar").expectBody(String.class).isEqualTo("FOO"); + this.client.post() + .uri("/") + .body(Mono.just("foo"), String.class) + .exchange() + .expectStatus() + .is2xxSuccessful() + .expectHeader() + .valueEquals("x-content-type", "application/xml") + .expectHeader() + .valueEquals("foo", "bar") + .expectBody(String.class) + .isEqualTo("FOO"); } @SpringBootConfiguration - protected static class TestConfiguration - implements Function, Message> { + protected static class TestConfiguration implements Function, Message> { @Override public Message apply(Message request) { - Message message = MessageBuilder - .withPayload(request.getPayload().toUpperCase(Locale.ROOT)) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); + Message message = MessageBuilder.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 e2697b134..2ffb08170 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 @@ -45,8 +45,14 @@ public class ImplicitNonFunctionalTests { @Test public void words() throws Exception { - this.client.post().uri("/").body(Mono.just("foo"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("FOO"); + this.client.post() + .uri("/") + .body(Mono.just("foo"), String.class) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("FOO"); } @SpringBootConfiguration diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/MoreThenOneFunctionRootMappingTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/MoreThenOneFunctionRootMappingTests.java index 3e6464f18..7c4e7f925 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/MoreThenOneFunctionRootMappingTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/MoreThenOneFunctionRootMappingTests.java @@ -34,8 +34,7 @@ * @author Oleg Zhurakousky * */ -@SpringBootTest({ "spring.main.web-application-type=REACTIVE", - "spring.functional.enabled=false", +@SpringBootTest({ "spring.main.web-application-type=REACTIVE", "spring.functional.enabled=false", "spring.cloud.function.definition=uppercase|reverse" }) @AutoConfigureWebTestClient @DirtiesContext @@ -46,8 +45,14 @@ public class MoreThenOneFunctionRootMappingTests { @Test public void words() { - this.client.post().uri("/").body(Mono.just("star"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("RATS"); + this.client.post() + .uri("/") + .body(Mono.just("star"), String.class) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("RATS"); } @SpringBootConfiguration 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 34c3273bb..190eca018 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 @@ -42,18 +42,26 @@ public class PojoTests { @Test public void single() throws Exception { - this.client.post().uri("/").body(Mono.just("{\"value\":\"foo\"}"), String.class) - .exchange().expectStatus().isOk().expectBody(String.class) - .isEqualTo("{\"value\":\"FOO\"}"); + this.client.post() + .uri("/") + .body(Mono.just("{\"value\":\"foo\"}"), String.class) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("{\"value\":\"FOO\"}"); } @Test public void multiple() throws Exception { - this.client.post().uri("/") - .body(Mono.just("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), - String.class) - .exchange().expectStatus().isOk().expectBody(String.class) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + this.client.post() + .uri("/") + .body(Mono.just("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @SpringBootConfiguration diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HeadersToMessageTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HeadersToMessageTests.java index 28c80c901..fb932a6e2 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HeadersToMessageTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HeadersToMessageTests.java @@ -45,9 +45,8 @@ * @author Adrien Poupard * */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.cloud.function.web.path=/functions", - "spring.main.web-application-type=reactive" }) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "spring.cloud.function.web.path=/functions", "spring.main.web-application-type=reactive" }) @ContextConfiguration(classes = { RestApplication.class, HeadersToMessageTests.TestConfiguration.class }) @AutoConfigureTestRestTemplate public class HeadersToMessageTests { @@ -58,23 +57,20 @@ public class HeadersToMessageTests { @Test public void testBodyAndCustomHeaderFromMessagePropagation() throws Exception { // test POJO paylod - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/employee")) - .contentType(MediaType.APPLICATION_JSON) - .body("{\"name\":\"Bob\",\"age\":25}"), Map.class); + ResponseEntity postForEntity = this.rest.exchange(RequestEntity.post(new URI("/functions/employee")) + .contentType(MediaType.APPLICATION_JSON) + .body("{\"name\":\"Bob\",\"age\":25}"), Map.class); assertThat(postForEntity.getBody()).containsExactlyInAnyOrderEntriesOf(Map.of("name", "Bob", "age", 25)); assertThat(postForEntity.getHeaders().containsHeader("x-content-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-content-type").get(0)) - .isEqualTo("application/xml"); + assertThat(postForEntity.getHeaders().get("x-content-type").get(0)).isEqualTo("application/xml"); assertThat(postForEntity.getHeaders().get("foo").get(0)).isEqualTo("bar"); // test simple type payload - postForEntity = this.rest.postForEntity(new URI("/functions/string"), - "{\"name\":\"Bob\",\"age\":25}", Map.class); + postForEntity = this.rest.postForEntity(new URI("/functions/string"), "{\"name\":\"Bob\",\"age\":25}", + Map.class); assertThat(postForEntity.getBody()).containsExactlyInAnyOrderEntriesOf(Map.of("name", "Bob", "age", 25)); assertThat(postForEntity.getHeaders().containsHeader("x-content-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-content-type").get(0)) - .isEqualTo("application/xml"); + assertThat(postForEntity.getHeaders().get("x-content-type").get(0)).isEqualTo("application/xml"); assertThat(postForEntity.getHeaders().get("foo").get(0)).isEqualTo("bar"); } @@ -86,8 +82,9 @@ protected static class TestConfiguration { public Function, Message> functiono() { return request -> { Message message = MessageBuilder.withPayload(request.getPayload()) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); + .setHeader("X-Content-Type", "application/xml") + .setHeader("foo", "bar") + .build(); return message; }; } @@ -95,10 +92,10 @@ public Function, Message> functiono() { @Bean({ "employee" }) public Function, Message> function1() { return request -> { - Message message = MessageBuilder - .withPayload(request.getPayload()) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); + Message message = MessageBuilder.withPayload(request.getPayload()) + .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/web/flux/HttpGetIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpGetIntegrationTests.java index c2afd53bd..2559791b4 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 @@ -58,7 +58,8 @@ /** * @author Dave Syer */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"spring.main.web-application-type=reactive", "debug=true"}) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "spring.main.web-application-type=reactive", "debug=true" }) @ContextConfiguration(classes = { RestApplication.class, ApplicationConfiguration.class }) @DirtiesContext @AutoConfigureTestRestTemplate @@ -82,71 +83,63 @@ public void init() { @Test public void staticResource() { - assertThat(this.rest.getForObject("/test.html", String.class)) - .contains("Test"); + assertThat(this.rest.getForObject("/test.html", String.class)).contains("Test"); } @Test public void wordsSSE() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), - String.class).getBody()).isEqualTo(sse("foo", "bar")); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), String.class) + .getBody()).isEqualTo(sse("foo", "bar")); } @Test public void wordsJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/words")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\",\"bar\"]"); + .exchange(RequestEntity.get(new URI("/words")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test public void errorJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/bang")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); + .exchange(RequestEntity.get(new URI("/bang")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\"]"); } @Test public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/words")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test public void word() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/word")).accept(MediaType.TEXT_PLAIN).build(), - String.class); + ResponseEntity result = this.rest + .exchange(RequestEntity.get(new URI("/word")).accept(MediaType.TEXT_PLAIN).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("foo"); } @Test public void foos() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/foos")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/foos")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); } @Test public void getMore() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/get/more")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/get/more")).build(), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test public void bareWords() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/bareWords")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/bareWords")).build(), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @@ -154,101 +147,85 @@ public void bareWords() throws Exception { @Test public void timeoutJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/timeout")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); + .exchange(RequestEntity.get(new URI("/timeout")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\"]"); } @Test public void emptyJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/empty")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[]"); + .exchange(RequestEntity.get(new URI("/empty")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[]"); } @Test public void sentences() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/sentences")).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/sentences")).build(), String.class).getBody()) + .isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); } @Test public void sentencesAcceptAny() throws Exception { assertThat( - this.rest - .exchange(RequestEntity.get(new URI("/sentences")) - .accept(MediaType.ALL).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); + this.rest.exchange(RequestEntity.get(new URI("/sentences")).accept(MediaType.ALL).build(), String.class) + .getBody()) + .isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); } @Test public void sentencesAcceptJson() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.get(new URI("/sentences")) - .accept(MediaType.APPLICATION_JSON).build(), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.get(new URI("/sentences")).accept(MediaType.APPLICATION_JSON).build(), String.class); assertThat(result.getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - assertThat(result.getHeaders().getContentType()) - .isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); + assertThat(result.getHeaders().getContentType()).isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); } @Test public void sentencesAcceptSse() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/sentences")).accept(EVENT_STREAM).build(), - String.class); - assertThat(result.getBody()) - .isEqualTo(sse("[\"go\",\"home\"]", "[\"come\",\"back\"]")); - assertThat(result.getHeaders().getContentType().isCompatibleWith(EVENT_STREAM)) - .isTrue(); + ResponseEntity result = this.rest + .exchange(RequestEntity.get(new URI("/sentences")).accept(EVENT_STREAM).build(), String.class); + assertThat(result.getBody()).isEqualTo(sse("[\"go\",\"home\"]", "[\"come\",\"back\"]")); + assertThat(result.getHeaders().getContentType().isCompatibleWith(EVENT_STREAM)).isTrue(); } @Test public void postMoreFoo() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/post/more/foo")).accept(MediaType.TEXT_PLAIN).build(), - String.class); + ResponseEntity result = this.rest + .exchange(RequestEntity.get(new URI("/post/more/foo")).accept(MediaType.TEXT_PLAIN).build(), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\"]"); } @Test public void uppercaseGet() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/uppercase/foo")).accept(MediaType.TEXT_PLAIN).build(), - String.class); + ResponseEntity result = this.rest + .exchange(RequestEntity.get(new URI("/uppercase/foo")).accept(MediaType.TEXT_PLAIN).build(), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\"]"); } @Test public void convertGet() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/wrap/123")).accept(MediaType.TEXT_PLAIN).build(), - String.class); + ResponseEntity result = this.rest + .exchange(RequestEntity.get(new URI("/wrap/123")).accept(MediaType.TEXT_PLAIN).build(), String.class); assertThat(result.getBody()).isEqualTo("[\"..123..\"]"); } @Test public void supplierFirst() { - assertThat(this.rest.getForObject("/not/a/function", String.class)) - .isEqualTo("[\"hello\"]"); + assertThat(this.rest.getForObject("/not/a/function", String.class)).isEqualTo("[\"hello\"]"); } @Test public void convertGetJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/entity/321")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[{\"value\":321}]"); + .exchange(RequestEntity.get(new URI("/entity/321")).accept(MediaType.APPLICATION_JSON).build(), + String.class) + .getBody()).isEqualTo("[{\"value\":321}]"); } @Test public void compose() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/concat,reverse/foo")).accept(MediaType.TEXT_PLAIN).build(), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.get(new URI("/concat,reverse/foo")).accept(MediaType.TEXT_PLAIN).build(), String.class); assertThat(result.getBody()).isEqualTo("[\"oofoof\"]"); } @@ -263,8 +240,7 @@ public static class ApplicationConfiguration { private List list = new ArrayList<>(); public static void main(String[] args) throws Exception { - SpringApplication.run(HttpGetIntegrationTests.ApplicationConfiguration.class, - args); + SpringApplication.run(HttpGetIntegrationTests.ApplicationConfiguration.class, args); } @Bean @@ -274,14 +250,12 @@ public Function, Flux> concat() { @Bean public Function, Flux> reverse() { - return flux -> flux.log() - .map(value -> new StringBuilder(value.trim()).reverse().toString()); + return flux -> flux.log().map(value -> new StringBuilder(value.trim()).reverse().toString()); } @Bean({ "uppercase", "post/more" }) public Function, Flux> uppercase() { - return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); + return flux -> flux.log().map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); } @Bean @@ -291,8 +265,7 @@ public Function, Flux> wrap() { @Bean public Function, Flux>> entity() { - return flux -> flux.log() - .map(value -> Collections.singletonMap("value", value)); + return flux -> flux.log().map(value -> Collections.singletonMap("value", value)); } @Bean({ "words", "get/more" }) @@ -349,15 +322,15 @@ public Supplier> timeout() { @Bean public Supplier>> sentences() { - return () -> Flux.just(Arrays.asList("go", "home"), - Arrays.asList("come", "back")); + return () -> Flux.just(Arrays.asList("go", "home"), Arrays.asList("come", "back")); } @Bean public Function, Map> sum() { - return valueMap -> valueMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, values -> values - .getValue().stream().mapToInt(Integer::parseInt).sum())); + return valueMap -> valueMap.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, + values -> values.getValue().stream().mapToInt(Integer::parseInt).sum())); } } 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 99581ab35..648c909ff 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 @@ -94,20 +94,19 @@ public void done() { @Test @DirtiesContext public void qualifierFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/foos")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.post(new URI("/foos")).contentType(MediaType.APPLICATION_JSON).body("[\"foo\",\"bar\"]"), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"[FOO]\"},{\"value\":\"[BAR]\"}]"); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"[FOO]\"},{\"value\":\"[BAR]\"}]"); } @Test @DirtiesContext public void updates() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\", \"two\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/updates")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"one\", \"two\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isNull(); @@ -116,9 +115,9 @@ public void updates() throws Exception { @Test @DirtiesContext public void updatesJson() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/updates")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"one\",\"two\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isEqualTo(null); @@ -128,9 +127,9 @@ public void updatesJson() throws Exception { @DirtiesContext @Disabled public void addFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/addFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isEqualTo(null); @@ -139,9 +138,9 @@ public void addFoos() throws Exception { @Test @DirtiesContext public void addFoosFlux() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoosFlux")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/addFoosFlux")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isEqualTo(null); @@ -151,9 +150,9 @@ public void addFoosFlux() throws Exception { @DirtiesContext @Disabled public void bareUpdates() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpdates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/bareUpdates")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"one\",\"two\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); } @@ -161,18 +160,19 @@ public void bareUpdates() throws Exception { @Test @DirtiesContext public void uppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/uppercase")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); } @Test @DirtiesContext public void messages() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/messages")).contentType(MediaType.APPLICATION_JSON) - .header("x-foo", "bar").body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/messages")) + .contentType(MediaType.APPLICATION_JSON) + .header("x-foo", "bar") + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getHeaders().getFirst("x-foo")).isEqualTo("bar"); assertThat(result.getHeaders().containsHeader("id")).isFalse(); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); @@ -181,20 +181,17 @@ public void messages() throws Exception { @Test @DirtiesContext public void headers() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/headers")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/headers")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); } @Test @DirtiesContext public void uppercaseSingleValue() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/uppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo"), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.post(new URI("/uppercase")).contentType(MediaType.TEXT_PLAIN).body("foo"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\"]"); } @@ -202,8 +199,7 @@ public void uppercaseSingleValue() throws Exception { @Disabled("WebFlux would split the request body into lines: TODO make this work the same") public void uppercasePlainText() throws Exception { ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/uppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo\nbar"), + RequestEntity.post(new URI("/uppercase")).contentType(MediaType.TEXT_PLAIN).body("foo\nbar"), String.class); assertThat(result.getBody()).isEqualTo("(FOO\nBAR)"); } @@ -211,41 +207,38 @@ public void uppercasePlainText() throws Exception { @Test @DirtiesContext public void uppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/upFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test @DirtiesContext public void uppercaseFoo() throws Exception { // Single Foo can be parsed - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("{\"value\":\"foo\"}"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/upFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("{\"value\":\"foo\"}"), String.class); assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"}]"); } @Test @DirtiesContext public void bareUppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/bareUpFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test @DirtiesContext @Disabled // not sure if this test is correct. Why does ? has to be assumed as String? public void typelessFunctionPassingArray() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/typelessFunctionExpectingText")) - .contentType(MediaType.TEXT_PLAIN).body("[{\"value\":\"foo\"}]"), - String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/typelessFunctionExpectingText")) + .contentType(MediaType.TEXT_PLAIN) + .body("[{\"value\":\"foo\"}]"), String.class); assertThat(result.getBody()).isEqualTo("[{\"value\":\"foo\"}]"); } @@ -254,46 +247,46 @@ public void typelessFunctionPassingArray() throws Exception { public void bareUppercaseFoo() throws Exception { // Single Foo can be parsed and returns a single value if the function is defined // that way - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpFoos")).contentType(MediaType.APPLICATION_JSON) - .body("{\"value\":\"foo\"}"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/bareUpFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("{\"value\":\"foo\"}"), String.class); assertThat(result.getBody()).isEqualTo("{\"value\":\"FOO\"}"); } @Test @DirtiesContext public void bareUppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/bareUppercase")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); } @Test @DirtiesContext public void singleValuedText() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/bareUppercase")).accept(MediaType.TEXT_PLAIN) - .contentType(MediaType.TEXT_PLAIN).body("foo"), - String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/bareUppercase")) + .accept(MediaType.TEXT_PLAIN) + .contentType(MediaType.TEXT_PLAIN) + .body("foo"), String.class); assertThat(result.getBody()).isEqualTo("(FOO)"); } @Test @DirtiesContext public void transform() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/transform")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/transform")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); } @Test @DirtiesContext public void postMore() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/post/more")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/post/more")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); } @@ -301,10 +294,7 @@ public void postMore() throws Exception { @DirtiesContext public void convertPost() throws Exception { ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/wrap")) - .contentType(MediaType.TEXT_PLAIN).body("123"), - String.class); + .exchange(RequestEntity.post(new URI("/wrap")).contentType(MediaType.TEXT_PLAIN).body("123"), String.class); // Result is multi-valued so it has to come out as an array assertThat(result.getBody()).isEqualTo("[\"..123..\"]"); } @@ -314,34 +304,34 @@ public void convertPost() throws Exception { public void convertPostJson() throws Exception { // If you POST a single value to a Function,Flux> it can't // determine if the output is single valued, so it has to send an array back - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/doubler")) - .contentType(MediaType.TEXT_PLAIN).body("123"), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.post(new URI("/doubler")).contentType(MediaType.TEXT_PLAIN).body("123"), String.class); assertThat(result.getBody()).isEqualTo("[246]"); } @Test @DirtiesContext public void uppercaseJsonArray() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/maps")) - .contentType(MediaType.APPLICATION_JSON) - // The new line in the middle is optional - .body("[{\"value\":\"foo\"},\n{\"value\":\"bar\"}]"), - String.class).getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + assertThat(this.rest.exchange(RequestEntity.post(new URI("/maps")) + .contentType(MediaType.APPLICATION_JSON) + // The new line in the middle is optional + .body("[{\"value\":\"foo\"},\n{\"value\":\"bar\"}]"), String.class).getBody()) + .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test @DirtiesContext public void uppercaseSSE() throws Exception { - String s = this.rest.exchange(RequestEntity.post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody(); - assertThat(this.rest.exchange(RequestEntity.post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("(FOO)", "(BAR)")); + String s = this.rest + .exchange(RequestEntity.post(new URI("/uppercase")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class) + .getBody(); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/uppercase")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class) + .getBody()).isEqualTo(sse("(FOO)", "(BAR)")); } @Test @@ -353,10 +343,12 @@ public void sum() throws Exception { map.put("A", Arrays.asList("1", "2", "3")); map.put("B", Arrays.asList("5", "6")); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/sum")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_FORM_URLENCODED).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/sum")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .body(map), String.class) + .getBody()).isEqualTo("{\"A\":6,\"B\":11}"); } @Test @@ -369,30 +361,36 @@ public void multipart() throws Exception { map.put("A", Arrays.asList("1", "2", "3")); map.put("B", Arrays.asList("5", "6")); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/sum")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/sum")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(map), String.class) + .getBody()).isEqualTo("{\"A\":6,\"B\":11}"); } @Test @DirtiesContext public void count() throws Exception { List list = Arrays.asList("A", "B", "A"); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/count")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON).body(list), - String.class).getBody()).isEqualTo("{\"A\":2,\"B\":1}"); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/count")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .body(list), String.class) + .getBody()).isEqualTo("{\"A\":2,\"B\":1}"); } @Test @DirtiesContext public void fluxWithList() throws Exception { List list = Arrays.asList("A", "B", "A"); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/fluxCollectionEcho")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON).body(list), - String.class).getBody()).isEqualTo("[\"A\",\"B\",\"A\"]"); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/fluxCollectionEcho")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .body(list), String.class) + .getBody()).isEqualTo("[\"A\",\"B\",\"A\"]"); } @Test @@ -416,8 +414,7 @@ public static class ApplicationConfiguration { private static int functionReactiveInvocations; public static void main(String[] args) throws Exception { - SpringApplication.run(HttpPostIntegrationTests.ApplicationConfiguration.class, - args); + SpringApplication.run(HttpPostIntegrationTests.ApplicationConfiguration.class, args); } @Bean @@ -435,8 +432,7 @@ public Consumer consumerImperative() { @Bean({ "uppercase", "transform", "post/more" }) public Function, Flux> uppercase() { - return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); + return flux -> flux.log().map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); } @Bean @@ -446,22 +442,22 @@ public Function bareUppercase() { @Bean public Function, Message> messages() { - return value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase(Locale.ROOT) + ")") - .copyHeaders(value.getHeaders()).build(); + return value -> MessageBuilder.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(Locale.ROOT) + ")") - .setHeader("foo", "bar").build()); + return flux -> flux + .map(value -> MessageBuilder.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(Locale.ROOT))); + return flux -> flux.log().map(value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT))); } @Bean @@ -506,8 +502,8 @@ public Function qualifier() { @Bean public Consumer> updates() { return flux -> flux.subscribe(value -> { - this.list.add(value); - }); + this.list.add(value); + }); } @Bean @@ -536,15 +532,15 @@ public Function, Flux> function() { @Bean public Function, Map> sum() { - return valueMap -> valueMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, values -> values - .getValue().stream().mapToInt(Integer::parseInt).sum())); + return valueMap -> valueMap.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, + values -> values.getValue().stream().mapToInt(Integer::parseInt).sum())); } @Bean public Function, Mono>> count() { - return flux -> flux.collect(HashMap::new, - (map, word) -> map.merge(word, 1, Integer::sum)); + return flux -> flux.collect(HashMap::new, (map, word) -> map.merge(word, 1, Integer::sum)); } @Bean diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/PrefixTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/PrefixTests.java index 4724a17a8..28efe6f06 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/PrefixTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/PrefixTests.java @@ -43,9 +43,9 @@ * @author Dave Syer * */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.main.web-application-type=reactive", - "spring.cloud.function.web.path=/functions", "debug" }) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "spring.main.web-application-type=reactive", "spring.cloud.function.web.path=/functions", + "debug" }) @ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) @AutoConfigureTestRestTemplate public class PrefixTests { @@ -58,16 +58,15 @@ public class PrefixTests { @Test public void words() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/functions/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/functions/words")).build(), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test public void missing() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/words")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/SingletonTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/SingletonTests.java index c2b94416d..1b90aa22c 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/SingletonTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/SingletonTests.java @@ -61,8 +61,7 @@ public class SingletonTests { @Test public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/words")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @@ -76,17 +75,13 @@ public static BeanDefinitionRegistryPostProcessor processor() { return new BeanDefinitionRegistryPostProcessor() { @Override - public void postProcessBeanFactory( - ConfigurableListableBeanFactory beanFactory) - throws BeansException { + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } @Override - public void postProcessBeanDefinitionRegistry( - BeanDefinitionRegistry registry) throws BeansException { + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // Simulates what happens when you add a compiled function - RootBeanDefinition beanDefinition = new RootBeanDefinition( - MySupplier.class); + RootBeanDefinition beanDefinition = new RootBeanDefinition(MySupplier.class); registry.registerBeanDefinition("words", beanDefinition); } }; 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 13c6e86fe..a6405049b 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 @@ -32,7 +32,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * @author Chris Bono * @since 2.1 @@ -41,27 +40,29 @@ public class FunctionEndpointInitializerMVCTests { @Test public void testSingleFunctionMapping() throws Exception { - ConfigurableApplicationContext context = SpringApplication.run(ApplicationConfiguration.class, "--server.port=0"); + ConfigurableApplicationContext context = SpringApplication.run(ApplicationConfiguration.class, + "--server.port=0"); TestRestTemplate testRestTemplate = new TestRestTemplate(); String port = context.getEnvironment().getProperty("local.server.port"); ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase"), "stressed", String.class); + .postForEntity(new URI("http://localhost:" + port + "/uppercase"), "stressed", String.class); assertThat(response.getBody()).isEqualTo("STRESSED"); - response = testRestTemplate.postForEntity(new URI("http://localhost:" + port + "/reverse"), "stressed", String.class); + response = testRestTemplate.postForEntity(new URI("http://localhost:" + port + "/reverse"), "stressed", + String.class); assertThat(response.getBody()).isEqualTo("desserts"); } @Test public void testCompositionFunctionMapping() throws Exception { - ConfigurableApplicationContext context = SpringApplication.run(ApplicationConfiguration.class, "--server.port=0"); + ConfigurableApplicationContext context = SpringApplication.run(ApplicationConfiguration.class, + "--server.port=0"); TestRestTemplate testRestTemplate = new TestRestTemplate(); String port = context.getEnvironment().getProperty("local.server.port"); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase,lowercase,reverse"), "stressed", String.class); + ResponseEntity response = testRestTemplate.postForEntity( + new URI("http://localhost:" + port + "/uppercase,lowercase,reverse"), "stressed", String.class); assertThat(response.getBody()).isEqualTo("desserts"); } - @SpringBootApplication protected static class ApplicationConfiguration { @@ -79,6 +80,7 @@ public Function lowercase() { public Function reverse() { return s -> new StringBuilder(s).reverse().toString(); } + } } 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 bf6a38655..756d39fe0 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 @@ -52,11 +52,10 @@ import static org.awaitility.Awaitility.await; /** -* -* @author Oleg Zhurakousky -* @author Chris Bono -* @since 2.1 -*/ + * @author Oleg Zhurakousky + * @author Chris Bono + * @since 2.1 + */ public class FunctionEndpointInitializerTests { @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -73,7 +72,10 @@ public void testEmptyBodyRequestParameters() throws Exception { HttpEntity entity = new HttpEntity(headers); String urlTemplate = UriComponentsBuilder.fromUriString("http://localhost:" + port + "/nullPayload") - .queryParam("fname", "Jim").queryParam("lname", "Lahey").encode().toUriString(); + .queryParam("fname", "Jim") + .queryParam("lname", "Lahey") + .encode() + .toUriString(); ResponseEntity response = testRestTemplate.exchange(urlTemplate, HttpMethod.GET, entity, String.class); String res = response.getBody(); @@ -85,8 +87,8 @@ public void testEmptyBodyRequestParameters() throws Exception { public void testNonExistingFunction() throws Exception { int port = startServerAndWaitForPort(ApplicationConfiguration.class); TestRestTemplate testRestTemplate = new TestRestTemplate(); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/foo"), "stressed", String.class); + ResponseEntity response = testRestTemplate.postForEntity(new URI("http://localhost:" + port + "/foo"), + "stressed", String.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } @@ -95,7 +97,7 @@ public void testConsumerMapping() throws Exception { int port = startServerAndWaitForPort(ConsumerConfiguration.class); TestRestTemplate testRestTemplate = new TestRestTemplate(); ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase"), "stressed", String.class); + .postForEntity(new URI("http://localhost:" + port + "/uppercase"), "stressed", String.class); assertThat(response.getBody()).isNull(); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); } @@ -105,9 +107,10 @@ public void testSingleFunctionMapping() throws Exception { int port = startServerAndWaitForPort(ApplicationConfiguration.class); TestRestTemplate testRestTemplate = new TestRestTemplate(); ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase"), "stressed", String.class); + .postForEntity(new URI("http://localhost:" + port + "/uppercase"), "stressed", String.class); assertThat(response.getBody()).isEqualTo("STRESSED"); - response = testRestTemplate.postForEntity(new URI("http://localhost:" + port + "/reverse"), "stressed", String.class); + response = testRestTemplate.postForEntity(new URI("http://localhost:" + port + "/reverse"), "stressed", + String.class); assertThat(response.getBody()).isEqualTo("desserts"); } @@ -115,8 +118,8 @@ public void testSingleFunctionMapping() throws Exception { public void testCompositionFunctionMapping() throws Exception { int port = startServerAndWaitForPort(ApplicationConfiguration.class); TestRestTemplate testRestTemplate = new TestRestTemplate(); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase,lowercase,reverse"), "stressed", String.class); + ResponseEntity response = testRestTemplate.postForEntity( + new URI("http://localhost:" + port + "/uppercase,lowercase,reverse"), "stressed", String.class); assertThat(response.getBody()).isEqualTo("desserts"); } @@ -125,7 +128,7 @@ public void testGetWithtFunction() throws Exception { int port = startServerAndWaitForPort(ApplicationConfiguration.class); TestRestTemplate testRestTemplate = new TestRestTemplate(); ResponseEntity response = testRestTemplate - .getForEntity(new URI("http://localhost:" + port + "/reverse/stressed"), String.class); + .getForEntity(new URI("http://localhost:" + port + "/reverse/stressed"), String.class); assertThat(response.getBody()).isEqualTo("desserts"); } @@ -134,16 +137,15 @@ public void testGetWithtSupplier() throws Exception { int port = startServerAndWaitForPort(ApplicationConfiguration.class); TestRestTemplate testRestTemplate = new TestRestTemplate(); ResponseEntity response = testRestTemplate - .getForEntity(new URI("http://localhost:" + port + "/supplier"), String.class); + .getForEntity(new URI("http://localhost:" + port + "/supplier"), String.class); assertThat(response.getBody()).isEqualTo("Jim Lahey"); } private int startServerAndWaitForPort(Class primaryAppConfig, boolean functional) throws InterruptedException { ConfigurableApplicationContext context = functional ? FunctionalSpringApplication.run(primaryAppConfig, "--server.port=0") - : SpringApplication.run(primaryAppConfig, "--server.port=0"); - await() - .pollDelay(Duration.ofMillis(500)) + : SpringApplication.run(primaryAppConfig, "--server.port=0"); + await().pollDelay(Duration.ofMillis(500)) .pollInterval(Duration.ofMillis(500)) .atMost(Duration.ofSeconds(3)) .untilAsserted(() -> { @@ -158,8 +160,7 @@ private int startServerAndWaitForPort(Class primaryAppConfig) throws Interrup } @SpringBootConfiguration - protected static class ConsumerConfiguration - implements ApplicationContextInitializer { + protected static class ConsumerConfiguration implements ApplicationContextInitializer { public Consumer consume() { return v -> System.out.println(v); @@ -178,14 +179,15 @@ public void initialize(GenericApplicationContext applicationContext) { @EnableAutoConfiguration @Configuration protected static class BeansConfiguration { + @Bean public BiFunction, Map> nullPayload() { return (p, h) -> { return h; }; } - } + } @SpringBootConfiguration protected static class ApplicationConfiguration @@ -222,8 +224,7 @@ public void initialize(GenericApplicationContext applicationContext) { () -> new FunctionRegistration<>(lowercase()) .type(FunctionTypeUtils.functionType(String.class, String.class))); applicationContext.registerBean("supplier", FunctionRegistration.class, - () -> new FunctionRegistration<>(supplier()) - .type(FunctionTypeUtils.supplierType(String.class))); + () -> new FunctionRegistration<>(supplier()).type(FunctionTypeUtils.supplierType(String.class))); } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/HeadersResponseMappingTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/HeadersResponseMappingTests.java index 2edc30adc..470a64af5 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/HeadersResponseMappingTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/HeadersResponseMappingTests.java @@ -16,7 +16,6 @@ package org.springframework.cloud.function.web.function; - import java.net.URI; import java.util.Locale; import java.util.function.Function; @@ -41,8 +40,8 @@ public void test_1220() throws Exception { "--server.port=0"); TestRestTemplate testRestTemplate = new TestRestTemplate(); String port = context.getEnvironment().getProperty("local.server.port"); - ResponseEntity response = testRestTemplate.postForEntity( - new URI("http://localhost:" + port + "/uppercase"), new Person("John", "Doe"), String.class); + ResponseEntity response = testRestTemplate + .postForEntity(new URI("http://localhost:" + port + "/uppercase"), new Person("John", "Doe"), String.class); assertThat(response.getBody()).isEqualTo("JOHN"); } @@ -58,4 +57,5 @@ public Function uppercase() { } } + } 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 9315cf661..ba0cf96f0 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 @@ -33,7 +33,6 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * * @author Oleg Zhurakousky * @author Chris Bono * @since 2.1 @@ -46,8 +45,8 @@ public void testIssue274() throws Exception { int port = context.getEnvironment().getProperty("local.server.port", Integer.class); TestRestTemplate testRestTemplate = new TestRestTemplate(); Thread.sleep(200); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/echo"), "", String.class); + ResponseEntity response = testRestTemplate.postForEntity(new URI("http://localhost:" + port + "/echo"), + "", String.class); assertThat(response.getBody()).isNull(); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); } @@ -58,13 +57,12 @@ public void testIssue274WithData() throws Exception { int port = context.getEnvironment().getProperty("local.server.port", Integer.class); TestRestTemplate testRestTemplate = new TestRestTemplate(); Thread.sleep(200); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/echo"), "hello", String.class); + ResponseEntity response = testRestTemplate.postForEntity(new URI("http://localhost:" + port + "/echo"), + "hello", String.class); assertThat(response.getBody()).isEqualTo("HELLO"); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); } - @SpringBootApplication protected static class Issue274Configuration { @@ -72,6 +70,7 @@ protected static class Issue274Configuration { public Function echo() { 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 12e9d7311..184580b90 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 @@ -59,8 +59,8 @@ public class DefaultRouteTests { @Test @Disabled("FIXME") public void explicit() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/uppercase")).body("foo"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/uppercase")).body("foo"), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("FOO"); } @@ -68,8 +68,7 @@ public void explicit() throws Exception { @Test @Disabled("FIXME") public void implicit() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.post(new URI("/")).body("foo"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/")).body("foo"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("FOO"); } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/GeneralIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/GeneralIntegrationTests.java index f40a95b4d..c55fef384 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/GeneralIntegrationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/GeneralIntegrationTests.java @@ -34,8 +34,8 @@ import org.springframework.http.ResponseEntity; import static org.assertj.core.api.Assertions.assertThat; + /** - * * @author Oleg Zhurakousky */ public class GeneralIntegrationTests { @@ -47,24 +47,20 @@ public void testMappedAndUnmappedDeleteFunction() throws Exception { String port = context.getEnvironment().getProperty("local.server.port"); TestRestTemplate template = new TestRestTemplate(); - ResponseEntity result = template.exchange( - RequestEntity.delete(new URI("http://localhost:" + port + "/consumer1")) - .build(), Void.class); + ResponseEntity result = template + .exchange(RequestEntity.delete(new URI("http://localhost:" + port + "/consumer1")).build(), Void.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - result = template.exchange( - RequestEntity.delete(new URI("http://localhost:" + port + "/consumer2")) - .build(), Void.class); + result = template.exchange(RequestEntity.delete(new URI("http://localhost:" + port + "/consumer2")).build(), + Void.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); result = template.exchange( - RequestEntity.delete(new URI("http://localhost:" + port + "/function,consumer1")) - .build(), Void.class); + RequestEntity.delete(new URI("http://localhost:" + port + "/function,consumer1")).build(), Void.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); - result = template.exchange( - RequestEntity.delete(new URI("http://localhost:" + port + "/supplier")) - .build(), Void.class); + result = template.exchange(RequestEntity.delete(new URI("http://localhost:" + port + "/supplier")).build(), + Void.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); } @@ -75,48 +71,50 @@ public void testMappedAndUnmappedPostPutFunction() throws Exception { String port = context.getEnvironment().getProperty("local.server.port"); TestRestTemplate template = new TestRestTemplate(); - ResponseEntity result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/consumer1")).contentType(MediaType.APPLICATION_JSON) + ResponseEntity result = template + .exchange(RequestEntity.post(new URI("http://localhost:" + port + "/consumer1")) + .contentType(MediaType.APPLICATION_JSON) .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/consumer2")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + result = template.exchange(RequestEntity.post(new URI("http://localhost:" + port + "/consumer2")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(result.getBody()).isNull(); - result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/function,consumer1")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + result = template.exchange(RequestEntity.post(new URI("http://localhost:" + port + "/function,consumer1")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(result.getBody()).isNull(); - result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/function")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + result = template.exchange(RequestEntity.post(new URI("http://localhost:" + port + "/function")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/supplier")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + result = template.exchange(RequestEntity.post(new URI("http://localhost:" + port + "/supplier")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); } - @EnableAutoConfiguration protected static class MultipleConsumerConfiguration { @Bean public Consumer consumer1() { - return v -> { }; + return v -> { + }; } @Bean public Consumer consumer2() { - return v -> { }; + return v -> { + }; } @Bean @@ -128,5 +126,7 @@ public Function function() { public Supplier supplier() { return () -> ""; } + } + } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HeadersToMessageTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HeadersToMessageTests.java index ce17d68c2..eb5ce33d2 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HeadersToMessageTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HeadersToMessageTests.java @@ -45,9 +45,8 @@ * @author Oleg Zhurakousky * */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.main.web-application-type=servlet", - "spring.cloud.function.web.path=/functions" }) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "spring.main.web-application-type=servlet", "spring.cloud.function.web.path=/functions" }) @ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) @AutoConfigureTestRestTemplate public class HeadersToMessageTests { @@ -57,29 +56,26 @@ public class HeadersToMessageTests { @Test public void testBodyAndCustomHeaderFromMessagePropagation() throws Exception { - HttpEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/employee")) - .contentType(MediaType.APPLICATION_JSON) - .body("{\"name\":\"Bob\",\"age\":25}"), Map.class); + HttpEntity postForEntity = this.rest.exchange(RequestEntity.post(new URI("/functions/employee")) + .contentType(MediaType.APPLICATION_JSON) + .body("{\"name\":\"Bob\",\"age\":25}"), Map.class); assertThat(postForEntity.getBody()).containsExactlyInAnyOrderEntriesOf(Map.of("name", "Bob", "age", 25)); assertThat(postForEntity.getHeaders().containsHeader("x-content-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-content-type").get(0)) - .isEqualTo("application/xml"); + assertThat(postForEntity.getHeaders().get("x-content-type").get(0)).isEqualTo("application/xml"); assertThat(postForEntity.getHeaders().get("foo").get(0)).isEqualTo("bar"); } @Test public void testHeadersPropagatedByDefault() throws Exception { - HttpEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/vanilla")) - .contentType(MediaType.APPLICATION_JSON) - .header("x-context-type", "rubbish") - .body("{\"name\":\"Bob\",\"age\":25}"), Map.class); - assertThat(postForEntity.getBody()).containsExactlyInAnyOrderEntriesOf(Map.of("name", "Bob", "age", 25, "foo", "bar")); + HttpEntity postForEntity = this.rest.exchange(RequestEntity.post(new URI("/functions/vanilla")) + .contentType(MediaType.APPLICATION_JSON) + .header("x-context-type", "rubbish") + .body("{\"name\":\"Bob\",\"age\":25}"), Map.class); + assertThat(postForEntity.getBody()) + .containsExactlyInAnyOrderEntriesOf(Map.of("name", "Bob", "age", 25, "foo", "bar")); assertThat(postForEntity.getHeaders().containsHeader("x-context-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-context-type").get(0)) - .isEqualTo("rubbish"); + assertThat(postForEntity.getHeaders().get("x-context-type").get(0)).isEqualTo("rubbish"); } @EnableAutoConfiguration @@ -89,10 +85,10 @@ protected static class TestConfiguration { @Bean({ "employee" }) public Function>, Message>> function() { return request -> { - Message> message = MessageBuilder - .withPayload(request.getPayload()) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); + Message> message = MessageBuilder.withPayload(request.getPayload()) + .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/web/mvc/HttpDeleteIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpDeleteIntegrationTests.java index e4abbbaee..b98a68098 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpDeleteIntegrationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpDeleteIntegrationTests.java @@ -49,7 +49,7 @@ * @author Oleg Zhurakousky */ @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=servlet") -@ContextConfiguration(classes = {ApplicationConfiguration.class}) +@ContextConfiguration(classes = { ApplicationConfiguration.class }) @AutoConfigureTestRestTemplate public class HttpDeleteIntegrationTests { @@ -69,27 +69,24 @@ public void init() { @Test public void testDeleteConsumer() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.delete(new URI("/deleteConsumer/123")) - .build(), Void.class); + ResponseEntity result = this.rest.exchange(RequestEntity.delete(new URI("/deleteConsumer/123")).build(), + Void.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); } @Test public void testDeleteConsumerWithParameters() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.delete(new URI("/deleteConsumerAsMessage/123?foo=bar")) - .build(), Void.class); + ResponseEntity result = this.rest + .exchange(RequestEntity.delete(new URI("/deleteConsumerAsMessage/123?foo=bar")).build(), Void.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); } @Test public void testDeleteWithFunction() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.delete(new URI("/deleteFunction")) - .build(), Void.class); + ResponseEntity result = this.rest.exchange(RequestEntity.delete(new URI("/deleteFunction")).build(), + Void.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); } @@ -100,8 +97,7 @@ public static class ApplicationConfiguration { private List list = new ArrayList<>(); public static void main(String[] args) throws Exception { - SpringApplication.run(HttpDeleteIntegrationTests.ApplicationConfiguration.class, - args); + SpringApplication.run(HttpDeleteIntegrationTests.ApplicationConfiguration.class, args); } @Bean @@ -113,7 +109,6 @@ public Function deleteFunction() { }; } - @Bean public Consumer deleteConsumer() { return v -> { 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 367a0770c..bfd11de95 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 @@ -87,82 +87,73 @@ public void init() { @Test public void staticResource() { - assertThat(this.rest.getForObject("/test.html", String.class)) - .contains("Test"); + assertThat(this.rest.getForObject("/test.html", String.class)).contains("Test"); } @Test public void wordsSSE() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), - String.class).getBody()).isEqualTo(sse("foo", "bar")); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), String.class) + .getBody()).isEqualTo(sse("foo", "bar")); } @Test public void wordsJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/words")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\",\"bar\"]"); + .exchange(RequestEntity.get(new URI("/words")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test @Disabled("Fix error handling") public void errorJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/bang")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); + .exchange(RequestEntity.get(new URI("/bang")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\"]"); } @Test public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/words")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test public void word() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/word")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/word")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("foo"); } @ParameterizedTest - @ValueSource(strings = {"[hello", "hello]", "[hello]"}) + @ValueSource(strings = { "[hello", "hello]", "[hello]" }) void textContentTypeWithValueWrappedBracketsIsOk(String inputMessagePayloadValue) throws URISyntaxException { - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/echo")) - .contentType(MediaType.TEXT_PLAIN) - .body(inputMessagePayloadValue), String.class); + ResponseEntity postForEntity = this.rest.exchange( + RequestEntity.post(new URI("/echo")).contentType(MediaType.TEXT_PLAIN).body(inputMessagePayloadValue), + String.class); assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(postForEntity.getBody()).isEqualTo(inputMessagePayloadValue); } @Test public void foos() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/foos")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/foos")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); } @Test public void getMore() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/get/more")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/get/more")).build(), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test public void bareWords() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/bareWords")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/bareWords")).build(), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @@ -170,99 +161,84 @@ public void bareWords() throws Exception { @Test public void timeoutJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/timeout")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); + .exchange(RequestEntity.get(new URI("/timeout")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[\"foo\"]"); } @Test public void emptyJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/empty")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[]"); + .exchange(RequestEntity.get(new URI("/empty")).accept(MediaType.APPLICATION_JSON).build(), String.class) + .getBody()).isEqualTo("[]"); } @Test public void sentences() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/sentences")).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); + assertThat(this.rest.exchange(RequestEntity.get(new URI("/sentences")).build(), String.class).getBody()) + .isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); } @Test public void sentencesAcceptAny() throws Exception { assertThat( - this.rest - .exchange(RequestEntity.get(new URI("/sentences")) - .accept(MediaType.ALL).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); + this.rest.exchange(RequestEntity.get(new URI("/sentences")).accept(MediaType.ALL).build(), String.class) + .getBody()) + .isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); } @Test public void sentencesAcceptJson() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.get(new URI("/sentences")) - .accept(MediaType.APPLICATION_JSON).build(), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.get(new URI("/sentences")).accept(MediaType.APPLICATION_JSON).build(), String.class); assertThat(result.getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - assertThat(result.getHeaders().getContentType()) - .isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); + assertThat(result.getHeaders().getContentType()).isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); } @Test @Disabled public void sentencesAcceptSse() throws Exception { Thread.sleep(1000); - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/sentences")).accept(EVENT_STREAM).build(), - String.class); - assertThat(result.getBody()) - .isEqualTo(sse("[\"go\",\"home\"]", "[\"come\",\"back\"]")); - assertThat(result.getHeaders().getContentType().isCompatibleWith(EVENT_STREAM)) - .isTrue(); + ResponseEntity result = this.rest + .exchange(RequestEntity.get(new URI("/sentences")).accept(EVENT_STREAM).build(), String.class); + assertThat(result.getBody()).isEqualTo(sse("[\"go\",\"home\"]", "[\"come\",\"back\"]")); + assertThat(result.getHeaders().getContentType().isCompatibleWith(EVENT_STREAM)).isTrue(); } @Test public void postMoreFoo() { - assertThat(this.rest.getForObject("/post/more/foo", String.class)) - .isEqualTo("[\"(FOO)\"]"); + assertThat(this.rest.getForObject("/post/more/foo", String.class)).isEqualTo("[\"(FOO)\"]"); } @Test public void uppercaseGet() { - assertThat(this.rest.getForObject("/uppercase/foo", String.class)) - .isEqualTo("[\"(FOO)\"]"); + assertThat(this.rest.getForObject("/uppercase/foo", String.class)).isEqualTo("[\"(FOO)\"]"); } @Test public void convertGet() { - assertThat(this.rest.getForObject("/wrap/123", String.class)) - .isEqualTo("[\"..123..\"]"); + assertThat(this.rest.getForObject("/wrap/123", String.class)).isEqualTo("[\"..123..\"]"); } @Test public void supplierFirst() { - assertThat(this.rest.getForObject("/not/a/function", String.class)) - .isEqualTo("[\"hello\"]"); + assertThat(this.rest.getForObject("/not/a/function", String.class)).isEqualTo("[\"hello\"]"); } @Test public void convertGetJson() throws Exception { assertThat(this.rest - .exchange(RequestEntity.get(new URI("/entity/321")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[{\"value\":321}]"); + .exchange(RequestEntity.get(new URI("/entity/321")).accept(MediaType.APPLICATION_JSON).build(), + String.class) + .getBody()).isEqualTo("[{\"value\":321}]"); } @Test @Disabled - // this test is wrong since it is returning Flux while setting CT to TEXT_PLAIN. We can't convert it + // this test is wrong since it is returning Flux while setting CT to TEXT_PLAIN. We + // can't convert it public void compose() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/concat,reverse/foo")).accept(MediaType.TEXT_PLAIN).build(), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.get(new URI("/concat,reverse/foo")).accept(MediaType.TEXT_PLAIN).build(), String.class); assertThat(result.getBody()).isEqualTo("oofoof"); } @@ -277,8 +253,7 @@ public static class ApplicationConfiguration { private List list = new ArrayList<>(); public static void main(String[] args) throws Exception { - SpringApplication.run(HttpGetIntegrationTests.ApplicationConfiguration.class, - args); + SpringApplication.run(HttpGetIntegrationTests.ApplicationConfiguration.class, args); } @Bean @@ -288,14 +263,12 @@ public Function, Flux> concat() { @Bean public Function, Flux> reverse() { - return flux -> flux.log() - .map(value -> new StringBuilder(value.trim()).reverse().toString()); + return flux -> flux.log().map(value -> new StringBuilder(value.trim()).reverse().toString()); } @Bean({ "uppercase", "post/more" }) public Function, Flux> uppercase() { - return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); + return flux -> flux.log().map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); } @Bean @@ -305,8 +278,7 @@ public Function, Flux> wrap() { @Bean public Function, Flux>> entity() { - return flux -> flux.log() - .map(value -> Collections.singletonMap("value", value)); + return flux -> flux.log().map(value -> Collections.singletonMap("value", value)); } @Bean({ "words", "get/more" }) @@ -368,15 +340,15 @@ public Supplier> timeout() { @Bean public Supplier>> sentences() { - return () -> Flux.just(Arrays.asList("go", "home"), - Arrays.asList("come", "back")); + return () -> Flux.just(Arrays.asList("go", "home"), Arrays.asList("come", "back")); } @Bean public Function, Map> sum() { - return valueMap -> valueMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, values -> values - .getValue().stream().mapToInt(Integer::parseInt).sum())); + return valueMap -> valueMap.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, + values -> values.getValue().stream().mapToInt(Integer::parseInt).sum())); } } 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 9490a8f34..28cce655f 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 @@ -84,19 +84,18 @@ public void init() { @Test @Disabled public void qualifierFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/foos")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.post(new URI("/foos")).contentType(MediaType.APPLICATION_JSON).body("[\"foo\",\"bar\"]"), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"[FOO]\"},{\"value\":\"[BAR]\"}]"); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"[FOO]\"},{\"value\":\"[BAR]\"}]"); } @Test public void updates() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\", \"two\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/updates")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"one\", \"two\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isNull(); @@ -104,9 +103,9 @@ public void updates() throws Exception { @Test public void updatesJson() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/updates")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"one\",\"two\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isEqualTo(null); @@ -114,38 +113,39 @@ public void updatesJson() throws Exception { @Test public void addFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/addFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); assertThat(result.getBody()).isEqualTo(null); } -// @Test + // @Test public void bareUpdates() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpdates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/bareUpdates")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"one\",\"two\"]"), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); assertThat(this.test.list).hasSize(2); } @Test public void uppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/uppercase")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); } @Test public void messages() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/messages")).contentType(MediaType.APPLICATION_JSON) - // Remove this when Spring 5.0.8 is used - .accept(MediaType.valueOf("application/stream+json")) - .header("x-foo", "bar").body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/messages")) + .contentType(MediaType.APPLICATION_JSON) + // Remove this when Spring 5.0.8 is used + .accept(MediaType.valueOf("application/stream+json")) + .header("x-foo", "bar") + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getHeaders().getFirst("x-foo")).isEqualTo("bar"); assertThat(result.getHeaders().containsHeader("id")).isFalse(); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); @@ -153,11 +153,11 @@ public void messages() throws Exception { @Test public void headers() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/headers")).contentType(MediaType.APPLICATION_JSON) - // Remove this when Spring 5.0.8 is used - .accept(MediaType.valueOf("application/stream+json")) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/headers")) + .contentType(MediaType.APPLICATION_JSON) + // Remove this when Spring 5.0.8 is used + .accept(MediaType.valueOf("application/stream+json")) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getHeaders().getFirst("foo")).isEqualTo("bar"); assertThat(result.getHeaders().containsHeader("id")).isFalse(); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); @@ -165,11 +165,8 @@ public void headers() throws Exception { @Test public void uppercaseSingleValue() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/uppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo"), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.post(new URI("/uppercase")).contentType(MediaType.TEXT_PLAIN).body("foo"), String.class); // Result is multi-valued so it has to come out as an array assertThat(result.getBody()).isEqualTo("[\"(FOO)\"]"); } @@ -178,90 +175,82 @@ public void uppercaseSingleValue() throws Exception { @Disabled("WebFlux would split the request body into lines: TODO make this work the same") public void uppercasePlainText() throws Exception { ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/uppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo\nbar"), + RequestEntity.post(new URI("/uppercase")).contentType(MediaType.TEXT_PLAIN).body("foo\nbar"), String.class); assertThat(result.getBody()).isEqualTo("(FOO)(BAR)"); } @Test public void uppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/upFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test public void uppercaseFoo() throws Exception { // Single Foo can be parsed - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("{\"value\":\"foo\"}"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/upFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("{\"value\":\"foo\"}"), String.class); assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"}]"); } @Test public void bareUppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/bareUpFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); + assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test public void bareUppercaseFoo() throws Exception { // Single Foo can be parsed and returns a single value if the function is defined // that way - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpFoos")).contentType(MediaType.APPLICATION_JSON) - .body("{\"value\":\"foo\"}"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/bareUpFoos")) + .contentType(MediaType.APPLICATION_JSON) + .body("{\"value\":\"foo\"}"), String.class); assertThat(result.getBody()).isEqualTo("{\"value\":\"FOO\"}"); } @Test public void bareUppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/bareUppercase")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); } @Test public void singleValuedText() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/bareUppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo"), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.post(new URI("/bareUppercase")).contentType(MediaType.TEXT_PLAIN).body("foo"), + String.class); assertThat(result.getBody()).isEqualTo("(FOO)"); } @Test public void transform() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/transform")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/transform")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); } @Test public void postMore() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/post/more")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.post(new URI("/post/more")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class); assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); } @Test public void convertPost() throws Exception { ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/wrap")) - .contentType(MediaType.TEXT_PLAIN).body("123"), - String.class); + .exchange(RequestEntity.post(new URI("/wrap")).contentType(MediaType.TEXT_PLAIN).body("123"), String.class); // Result is multi-valued so it has to come out as an array assertThat(result.getBody()).isEqualTo("[\"..123..\"]"); } @@ -270,30 +259,27 @@ public void convertPost() throws Exception { public void convertPostJson() throws Exception { // If you POST a single value to a Function,Flux> it can't // determine if the output is single valued, so it has to send an array back - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/doubler")) - .contentType(MediaType.TEXT_PLAIN).body("123"), - String.class); + ResponseEntity result = this.rest.exchange( + RequestEntity.post(new URI("/doubler")).contentType(MediaType.TEXT_PLAIN).body("123"), String.class); assertThat(result.getBody()).isEqualTo("[246]"); } @Test public void uppercaseJsonArray() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/maps")) - .contentType(MediaType.APPLICATION_JSON) - // The new line in the middle is optional - .body("[{\"value\":\"foo\"},\n{\"value\":\"bar\"}]"), - String.class).getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); + assertThat(this.rest.exchange(RequestEntity.post(new URI("/maps")) + .contentType(MediaType.APPLICATION_JSON) + // The new line in the middle is optional + .body("[{\"value\":\"foo\"},\n{\"value\":\"bar\"}]"), String.class).getBody()) + .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test public void uppercaseSSE() throws Exception { - assertThat(this.rest.exchange(RequestEntity.post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("(FOO)", "(BAR)")); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/uppercase")) + .contentType(MediaType.APPLICATION_JSON) + .body("[\"foo\",\"bar\"]"), String.class) + .getBody()).isEqualTo(sse("(FOO)", "(BAR)")); } @Test @@ -304,10 +290,12 @@ public void sum() throws Exception { map.put("A", Arrays.asList("1", "2", "3")); map.put("B", Arrays.asList("5", "6")); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/sum")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/sum")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(map), String.class) + .getBody()).isEqualTo("{\"A\":6,\"B\":11}"); } @Test @@ -318,19 +306,23 @@ public void multipart() throws Exception { map.put("A", Arrays.asList("1", "2", "3")); map.put("B", Arrays.asList("5", "6")); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/sum")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/sum")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(map), String.class) + .getBody()).isEqualTo("{\"A\":6,\"B\":11}"); } @Test public void count() throws Exception { List list = Arrays.asList("A", "B", "A"); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/count")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON).body(list), - String.class).getBody()).isEqualTo("{\"A\":2,\"B\":1}"); + assertThat(this.rest + .exchange(RequestEntity.post(new URI("/count")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .body(list), String.class) + .getBody()).isEqualTo("{\"A\":2,\"B\":1}"); } private String sse(String... values) { @@ -344,14 +336,12 @@ public static class ApplicationConfiguration { private List list = new ArrayList<>(); public static void main(String[] args) throws Exception { - SpringApplication.run(HttpPostIntegrationTests.ApplicationConfiguration.class, - args); + SpringApplication.run(HttpPostIntegrationTests.ApplicationConfiguration.class, args); } @Bean({ "uppercase", "transform", "post/more" }) public Function, Flux> uppercase() { - return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); + return flux -> flux.log().map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); } @Bean @@ -361,22 +351,22 @@ public Function bareUppercase() { @Bean public Function, Message> messages() { - return value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase(Locale.ROOT) + ")") - .copyHeaders(value.getHeaders()).build(); + return value -> MessageBuilder.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(Locale.ROOT) + ")") - .setHeader("foo", "bar").build()); + return flux -> flux + .map(value -> MessageBuilder.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(Locale.ROOT))); + return flux -> flux.log().map(value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT))); } @Bean @@ -432,15 +422,15 @@ public Function, Flux> function() { @Bean public Function, Map> sum() { - return valueMap -> valueMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, values -> values - .getValue().stream().mapToInt(Integer::parseInt).sum())); + return valueMap -> valueMap.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, + values -> values.getValue().stream().mapToInt(Integer::parseInt).sum())); } @Bean public Function, Mono>> count() { - return flux -> flux.collect(HashMap::new, - (map, word) -> map.merge(word, 1, Integer::sum)); + return flux -> flux.collect(HashMap::new, (map, word) -> map.merge(word, 1, Integer::sum)); } } 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 1a9bdc394..52f2dd1d3 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 @@ -39,8 +39,8 @@ import org.springframework.web.multipart.MultipartFile; import static org.assertj.core.api.Assertions.assertThat; + /** - * * @author Oleg Zhurakousky * @author Chris Bono */ @@ -97,5 +97,7 @@ public Function uppercase() { return value.getOriginalFilename().toUpperCase(Locale.ROOT); }; } + } + } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/PrefixTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/PrefixTests.java index 65f4abdf6..c8d49266d 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/PrefixTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/PrefixTests.java @@ -46,9 +46,8 @@ * @author Dave Syer * */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.main.web-application-type=servlet", - "spring.cloud.function.web.path=/functions" }) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "spring.main.web-application-type=servlet", "spring.cloud.function.web.path=/functions" }) @ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) @AutoConfigureTestRestTemplate public class PrefixTests { @@ -58,20 +57,18 @@ public class PrefixTests { @Test public void words() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/functions/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/functions/words")).build(), + String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @Test public void missing() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/words")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } - @Test public void uppercase() throws Exception { ResponseEntity result = this.rest.exchange( @@ -93,7 +90,8 @@ public Supplier> words() { public Function, String[]> uppercase() { return message -> { assertThat(message.getPayload().equals("foo")); - Map httpParam = (Map) message.getHeaders().get(HeaderUtils.HTTP_REQUEST_PARAM); + Map httpParam = (Map) message.getHeaders() + .get(HeaderUtils.HTTP_REQUEST_PARAM); assertThat(httpParam.get("nome")).isEqualTo("Doe"); assertThat(httpParam.get("prenome")).isEqualTo("John"); return new String[] { "foo", "bar" }; 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 daeec303b..57ead79d9 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 @@ -53,11 +53,9 @@ * @author Oleg Zhurakousky * */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.main.web-application-type=servlet", - "spring.cloud.function.web.path=/functions", - "spring.cloud.function.routing.enabled=true", - "spring.cloud.function.http.ignored-headers=abc,xyz"}) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "spring.main.web-application-type=servlet", "spring.cloud.function.web.path=/functions", + "spring.cloud.function.routing.enabled=true", "spring.cloud.function.http.ignored-headers=abc,xyz" }) @ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) @AutoConfigureTestRestTemplate public class RoutingFunctionTests { @@ -68,22 +66,20 @@ public class RoutingFunctionTests { @Autowired private FunctionProperties functionProperties; - @Test @DirtiesContext public void testFunctionMessage() throws Exception { HttpEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.APPLICATION_JSON) - .header("spring.cloud.function.definition", "employee") - .header("abc", "abc") - .header("xyz", "xyz") - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); + .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) + .contentType(MediaType.APPLICATION_JSON) + .header("spring.cloud.function.definition", "employee") + .header("abc", "abc") + .header("xyz", "xyz") + .body("{\"name\":\"Bob\",\"age\":25}"), String.class); assertThat(postForEntity.getBody()).isEqualTo("{\"name\":\"Bob\",\"age\":25}"); assertThat(postForEntity.getHeaders().containsHeader("x-content-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-content-type").get(0)) - .isEqualTo("application/xml"); + assertThat(postForEntity.getHeaders().get("x-content-type").get(0)).isEqualTo("application/xml"); assertThat(postForEntity.getHeaders().containsHeader("spring.cloud.function.definition")).isTrue(); assertThat(postForEntity.getHeaders().containsHeader("abc")).isFalse(); assertThat(postForEntity.getHeaders().containsHeader("xyz")).isFalse(); @@ -94,15 +90,14 @@ public void testFunctionMessage() throws Exception { @DirtiesContext public void testFunctionPrimitive() throws Exception { ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .header("spring.cloud.function.definition", "echo") - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); - postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .header("spring.cloud.function.definition", "echo") - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); + .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) + .contentType(MediaType.TEXT_PLAIN) + .header("spring.cloud.function.definition", "echo") + .body("{\"name\":\"Bob\",\"age\":25}"), String.class); + postForEntity = this.rest.exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) + .contentType(MediaType.TEXT_PLAIN) + .header("spring.cloud.function.definition", "echo") + .body("{\"name\":\"Bob\",\"age\":25}"), String.class); assertThat(postForEntity.getBody()).isEqualTo("{\"name\":\"Bob\",\"age\":25}"); assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); @@ -114,23 +109,24 @@ public void testFunctionPrimitive() throws Exception { public void testFluxFunctionPrimitive() throws Exception { this.functionProperties.setDefinition("fluxuppercase"); ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .body("[\"hello\", \"bye\"]"), String.class); + .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) + .contentType(MediaType.TEXT_PLAIN) + .body("[\"hello\", \"bye\"]"), String.class); assertThat(postForEntity.getBody()).isEqualTo("[\"HELLO\", \"BYE\"]"); assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); postForEntity = this.rest.exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .body("hello1"), String.class); + .contentType(MediaType.TEXT_PLAIN) + .body("hello1"), String.class); assertThat(postForEntity.getBody()).isEqualTo("HELLO1"); assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); -// postForEntity = this.rest.exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) -// .contentType(MediaType.TEXT_PLAIN) -// .body("hello2"), String.class); -// assertThat(postForEntity.getBody()).isEqualTo("HELLO2"); -// assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); + // postForEntity = this.rest.exchange(RequestEntity.post(new URI("/functions/" + + // RoutingFunction.FUNCTION_NAME)) + // .contentType(MediaType.TEXT_PLAIN) + // .body("hello2"), String.class); + // assertThat(postForEntity.getBody()).isEqualTo("HELLO2"); + // assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); } @Test @@ -138,9 +134,9 @@ public void testFluxFunctionPrimitive() throws Exception { public void testFluxFunctionPrimitiveArray() throws Exception { this.functionProperties.setDefinition("fluxuppercase"); ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.APPLICATION_JSON) - .body(new String[] {"a", "b", "c"}), String.class); + .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) + .contentType(MediaType.APPLICATION_JSON) + .body(new String[] { "a", "b", "c" }), String.class); assertThat(postForEntity.getBody()).isEqualTo("[\"A\",\"B\",\"C\"]"); assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); } @@ -150,24 +146,23 @@ public void testFluxFunctionPrimitiveArray() throws Exception { @Disabled public void testFluxConsumer() throws Exception { ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.APPLICATION_JSON) - .header("function.name", "fluxconsumer") - .body(new String[] {"a", "b", "c"}), String.class); + .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) + .contentType(MediaType.APPLICATION_JSON) + .header("function.name", "fluxconsumer") + .body(new String[] { "a", "b", "c" }), String.class); assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); } - @Test @DirtiesContext @Disabled public void testFunctionPojo() throws Exception { ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.APPLICATION_JSON) - .header("function.name", "echoPojo") - .body("{\"value\":\"foo\"}"), String.class); + .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) + .contentType(MediaType.APPLICATION_JSON) + .header("function.name", "echoPojo") + .body("{\"value\":\"foo\"}"), String.class); assertThat(postForEntity.getBody()).isEqualTo("{\"foo\":{\"value\":\"foo\"},\"value\":\"bar\"}"); assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); } @@ -177,10 +172,10 @@ public void testFunctionPojo() throws Exception { @Disabled public void testConsumerMessage() throws Exception { ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .header("spring.cloud.function.definition", "messageConsumer") - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); + .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) + .contentType(MediaType.TEXT_PLAIN) + .header("spring.cloud.function.definition", "messageConsumer") + .body("{\"name\":\"Bob\",\"age\":25}"), String.class); assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); } @@ -191,10 +186,10 @@ protected static class TestConfiguration { @Bean({ "employee" }) public Function>, Message>> function() { return request -> { - Message> message = MessageBuilder - .withPayload(request.getPayload()) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); + Message> message = MessageBuilder.withPayload(request.getPayload()) + .setHeader("X-Content-Type", "application/xml") + .setHeader("foo", "bar") + .build(); return message; }; } @@ -237,6 +232,7 @@ public Function echoPojo() { } public static class Foo { + private String value; public String getValue() { @@ -246,23 +242,31 @@ public String getValue() { public void setValue(String value) { this.value = value; } + } public static class Bar { + private Foo foo; + private String value; + public Foo getFoo() { return foo; } + public void setFoo(Foo foo) { this.foo = foo; } + public String getValue() { return value; } + public void setValue(String value) { this.value = value; } + } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/SingletonTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/SingletonTests.java index b684a80ad..91720fe1d 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/SingletonTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/SingletonTests.java @@ -57,8 +57,7 @@ public class SingletonTests { @Test public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); + ResponseEntity result = this.rest.exchange(RequestEntity.get(new URI("/words")).build(), String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); } @@ -72,17 +71,13 @@ public static BeanDefinitionRegistryPostProcessor processor() { return new BeanDefinitionRegistryPostProcessor() { @Override - public void postProcessBeanFactory( - ConfigurableListableBeanFactory beanFactory) - throws BeansException { + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } @Override - public void postProcessBeanDefinitionRegistry( - BeanDefinitionRegistry registry) throws BeansException { + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // Simulates what happens when you add a compiled function - RootBeanDefinition beanDefinition = new RootBeanDefinition( - MySupplier.class); + RootBeanDefinition beanDefinition = new RootBeanDefinition(MySupplier.class); registry.registerBeanDefinition("words", beanDefinition); } }; 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 084d58b90..61736582b 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 @@ -51,13 +51,13 @@ * @author Dave Syer * */ -@SpringBootTest(classes = { RestConfiguration.class, - ApplicationConfiguration.class }, webEnvironment = WebEnvironment.DEFINED_PORT, properties = { - "spring.cloud.function.web.export.sink.url=http://localhost:${server.port}", +@SpringBootTest(classes = { RestConfiguration.class, ApplicationConfiguration.class }, + webEnvironment = WebEnvironment.DEFINED_PORT, + properties = { "spring.cloud.function.web.export.sink.url=http://localhost:${server.port}", "spring.cloud.function.web.export.source.url=http://localhost:${server.port}", "spring.cloud.function.web.export.sink.name=origin|uppercase" - // "spring.cloud.function.web.export.debug=true" - }) + // "spring.cloud.function.web.export.debug=true" + }) @Disabled public class FunctionAutoConfigurationIntegrationTests { 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 b01e21312..67c9e72c1 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 @@ -49,13 +49,12 @@ * @author Oleg Zhurakousky * */ -@SpringBootTest(classes = { RestConfiguration.class, - ApplicationConfiguration.class }, - webEnvironment = WebEnvironment.DEFINED_PORT, properties = { - "spring.cloud.function.web.export.sink.url=http://localhost:${server.port}", - "spring.cloud.function.web.export.source.url=http://localhost:${server.port}", - "spring.cloud.function.web.export.sink.name=origin|uppercase", - "spring.cloud.function.web.export.debug=true"}) +@SpringBootTest(classes = { RestConfiguration.class, ApplicationConfiguration.class }, + webEnvironment = WebEnvironment.DEFINED_PORT, + properties = { "spring.cloud.function.web.export.sink.url=http://localhost:${server.port}", + "spring.cloud.function.web.export.source.url=http://localhost:${server.port}", + "spring.cloud.function.web.export.sink.name=origin|uppercase", + "spring.cloud.function.web.export.debug=true" }) public class FunctionAutoConfigurationWithRetriesIntegrationTests { @Autowired diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/WebAppIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/WebAppIntegrationTests.java index 896cffad5..9f9706453 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/WebAppIntegrationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/WebAppIntegrationTests.java @@ -49,11 +49,11 @@ * */ @SpringBootTest(classes = { RestApplication.class, ApplicationConfiguration.class }, - webEnvironment = WebEnvironment.DEFINED_PORT, properties = { - "spring.main.web-application-type=reactive", - "spring.cloud.function.web.export.sink.url=http://localhost:${server.port}/values", - // manually so we know the webapp is listening when we start - "spring.cloud.function.web.export.autoStartup=false" }) + webEnvironment = WebEnvironment.DEFINED_PORT, + properties = { "spring.main.web-application-type=reactive", + "spring.cloud.function.web.export.sink.url=http://localhost:${server.port}/values", + // manually so we know the webapp is listening when we start + "spring.cloud.function.web.export.autoStartup=false" }) public class WebAppIntegrationTests { private static Log logger = LogFactory.getLog(WebAppIntegrationTests.class); From 08dcba619de6a4dfa92a30bfb01b9a516a226e7a Mon Sep 17 00:00:00 2001 From: Hyunwoo Jung Date: Thu, 20 Nov 2025 00:32:14 +0900 Subject: [PATCH 2/3] Refactor Json texture using text block and add checkstyle suppressions Signed-off-by: Hyunwoo Jung --- .../aws/CustomRuntimeEventLoopTest.java | 121 ++- .../adapter/aws/FunctionInvokerTests.java | 982 ++++++++++++------ .../serverless/web/RequestResponseTests.java | 18 +- .../cloudevent/CloudEventFunctionTests.java | 80 +- .../cloud/function/utils/JsonMaskerTests.java | 202 +++- src/checkstyle/checkstyle-suppressions.xml | 5 + 6 files changed, 1001 insertions(+), 407 deletions(-) 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 6337330a0..48741c216 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 @@ -40,43 +40,90 @@ */ public class CustomRuntimeEventLoopTest { - private String API_EVENT = "{\n" + " \"version\": \"1.0\",\n" + " \"resource\": \"$default\",\n" - + " \"path\": \"/question\",\n" + " \"httpMethod\": \"POST\",\n" + " \"headers\": {\n" - + " \"Content-Length\": \"40\",\n" + " \"Content-Type\": \"application/json\",\n" - + " \"Host\": \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\",\n" - + " \"User-Agent\": \"curl/7.88.1\",\n" - + " \"X-Amzn-Trace-Id\": \"Root=1-64ad9787-4c89d5af7607eb9e522e01d5\",\n" - + " \"X-Forwarded-For\": \"109.210.252.44\",\n" + " \"X-Forwarded-Port\": \"443\",\n" - + " \"X-Forwarded-Proto\": \"https\",\n" + " \"accept\": \"*/*\"\n" + " },\n" - + " \"multiValueHeaders\": {\n" + " \"Content-Length\": [\n" + " \"40\"\n" - + " ],\n" + " \"Content-Type\": [\n" + " \"application/json\"\n" + " ],\n" - + " \"Host\": [\n" + " \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\"\n" - + " ],\n" + " \"User-Agent\": [\n" + " \"curl/7.88.1\"\n" + " ],\n" - + " \"X-Amzn-Trace-Id\": [\n" + " \"Root=1-64ad9787-4c89d5af7607eb9e522e01d5\"\n" - + " ],\n" + " \"X-Forwarded-For\": [\n" + " \"109.210.252.44\"\n" + " ],\n" - + " \"X-Forwarded-Port\": [\n" + " \"443\"\n" + " ],\n" - + " \"X-Forwarded-Proto\": [\n" + " \"https\"\n" + " ],\n" - + " \"accept\": [\n" + " \"*/*\"\n" + " ]\n" + " },\n" - + " \"queryStringParameters\": null,\n" + " \"multiValueQueryStringParameters\": null,\n" - + " \"requestContext\": {\n" + " \"accountId\": \"313369169943\",\n" - + " \"apiId\": \"emcdxu5ijj\",\n" - + " \"domainName\": \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\",\n" - + " \"domainPrefix\": \"emcdxu5ijj\",\n" + " \"extendedRequestId\": \"H6SdPgXtiYcEP1w=\",\n" - + " \"httpMethod\": \"POST\",\n" + " \"identity\": {\n" + " \"accessKey\": null,\n" - + " \"accountId\": null,\n" + " \"caller\": null,\n" - + " \"cognitoAmr\": null,\n" + " \"cognitoAuthenticationProvider\": null,\n" - + " \"cognitoAuthenticationType\": null,\n" + " \"cognitoIdentityId\": null,\n" - + " \"cognitoIdentityPoolId\": null,\n" + " \"principalOrgId\": null,\n" - + " \"sourceIp\": \"109.210.252.44\",\n" + " \"user\": null,\n" - + " \"userAgent\": \"curl/7.88.1\",\n" + " \"userArn\": null\n" + " },\n" - + " \"path\": \"/question\",\n" + " \"protocol\": \"HTTP/1.1\",\n" - + " \"requestId\": \"H6SdPgXtiYcEP1w=\",\n" - + " \"requestTime\": \"11/Jul/2023:17:55:19 +0000\",\n" - + " \"requestTimeEpoch\": 1689098119662,\n" + " \"resourceId\": \"$default\",\n" - + " \"resourcePath\": \"$default\",\n" + " \"stage\": \"$default\"\n" + " },\n" - + " \"pathParameters\": null,\n" + " \"stageVariables\": null,\n" - + " \"body\": \"[{\\\"latitude\\\": 41.34, \\\"longitude\\\": 2.78},{\\\"latitude\\\": 43.24, \\\"longitude\\\": 3.78}]\",\n" - + " \"isBase64Encoded\": false\n" + "}"; + private String API_EVENT = """ + { + "version": "1.0", + "resource": "$default", + "path": "/question", + "httpMethod": "POST", + "headers": { + "Content-Length": "40", + "Content-Type": "application/json", + "Host": "emcdxu5ijj.execute-api.us-east-2.amazonaws.com", + "User-Agent": "curl/7.88.1", + "X-Amzn-Trace-Id": "Root=1-64ad9787-4c89d5af7607eb9e522e01d5", + "X-Forwarded-For": "109.210.252.44", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https", + "accept": "*/*" + }, + "multiValueHeaders": { + "Content-Length": [ + "40" + ], + "Content-Type": [ + "application/json" + ], + "Host": [ + "emcdxu5ijj.execute-api.us-east-2.amazonaws.com" + ], + "User-Agent": [ + "curl/7.88.1" + ], + "X-Amzn-Trace-Id": [ + "Root=1-64ad9787-4c89d5af7607eb9e522e01d5" + ], + "X-Forwarded-For": [ + "109.210.252.44" + ], + "X-Forwarded-Port": [ + "443" + ], + "X-Forwarded-Proto": [ + "https" + ], + "accept": [ + "*/*" + ] + }, + "queryStringParameters": null, + "multiValueQueryStringParameters": null, + "requestContext": { + "accountId": "313369169943", + "apiId": "emcdxu5ijj", + "domainName": "emcdxu5ijj.execute-api.us-east-2.amazonaws.com", + "domainPrefix": "emcdxu5ijj", + "extendedRequestId": "H6SdPgXtiYcEP1w=", + "httpMethod": "POST", + "identity": { + "accessKey": null, + "accountId": null, + "caller": null, + "cognitoAmr": null, + "cognitoAuthenticationProvider": null, + "cognitoAuthenticationType": null, + "cognitoIdentityId": null, + "cognitoIdentityPoolId": null, + "principalOrgId": null, + "sourceIp": "109.210.252.44", + "user": null, + "userAgent": "curl/7.88.1", + "userArn": null + }, + "path": "/question", + "protocol": "HTTP/1.1", + "requestId": "H6SdPgXtiYcEP1w=", + "requestTime": "11/Jul/2023:17:55:19 +0000", + "requestTimeEpoch": 1689098119662, + "resourceId": "$default", + "resourcePath": "$default", + "stage": "$default" + }, + "pathParameters": null, + "stageVariables": null, + "body": "[{\\"latitude\\": 41.34, \\"longitude\\": 2.78},{\\"latitude\\": 43.24, \\"longitude\\": 3.78}]", + "isBase64Encoded": false + }"""; @Test public void testDefaultFunctionLookup() throws Exception { 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 519b30dd2..65d618b98 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 @@ -79,301 +79,697 @@ public class FunctionInvokerTests { String jsonPojoCollection = "[{\"name\":\"Ricky\"},{\"name\":\"Julien\"},{\"name\":\"Julien\"}]"; - String someEvent = "{\n" + " \"payload\": {\n" + " \"headers\": {\n" - + " \"businessUnit\": \"1\"\n" + " }\n" + " },\n" + " \"headers\": {\n" - + " \"aws-context\": {\n" + " \"memoryLimit\": 1024,\n" - + " \"awsRequestId\": \"87a211bf-540f-4f9f-a218-d096a0099999\",\n" - + " \"functionName\": \"myfunction\",\n" + " \"functionVersion\": \"278\",\n" - + " \"invokedFunctionArn\": \"arn:aws:lambda:us-east-1:xxxxxxx:function:xxxxx:snapstart\",\n" - + " \"deadlineTimeInMs\": 1712717704761,\n" + " \"logger\": {\n" - + " \"logFiltering\": {\n" + " \"minimumLogLevel\": \"UNDEFINED\"\n" - + " },\n" + " \"logFormatter\": {},\n" - + " \"logFormat\": \"TEXT\"\n" + " }\n" + " },\n" - + " \"businessUnit\": \"1\",\n" + " \"id\": \"xxxx\",\n" + " \"aws-event\": true,\n" - + " \"timestamp\": 1712716805129\n" + " }\n" + "}"; - - String scheduleEvent = "{\n" + " \"version\": \"0\",\n" + " \"id\": \"17793124-05d4-b198-2fde-7ededc63b103\",\n" - + " \"detail-type\": \"Object Created\",\n" + " \"source\": \"aws.s3\",\n" - + " \"account\": \"111122223333\",\n" + " \"time\": \"2021-11-12T00:00:00Z\",\n" - + " \"region\": \"ca-central-1\",\n" + " \"resources\": [\n" - + " \"arn:aws:s3:::amzn-s3-demo-bucket1\"\n" + " ],\n" + " \"detail\": {\n" - + " \"version\": \"0\",\n" + " \"bucket\": {\n" + " \"name\": \"amzn-s3-demo-bucket1\"\n" - + " },\n" + " \"object\": {\n" + " \"key\": \"example-key\",\n" + " \"size\": 5,\n" - + " \"etag\": \"b1946ac92492d2347c6235b4d2611184\",\n" - + " \"version-id\": \"IYV3p45BT0ac8hjHg1houSdS1a.Mro8e\",\n" - + " \"sequencer\": \"617f08299329d189\"\n" + " },\n" + " \"request-id\": \"N4N7GDK58NMKJ12R\",\n" - + " \"requester\": \"123456789012\",\n" + " \"source-ip-address\": \"1.2.3.4\",\n" - + " \"reason\": \"PutObject\"\n" + " }\n" + "} "; - - String dynamoDbEvent = "{\n" + " \"Records\": [\n" + " {\n" - + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e69f274ee\",\n" + " \"eventName\": \"INSERT\",\n" - + " \"eventVersion\": \"1.1\",\n" + " \"eventSource\": \"aws:dynamodb\",\n" - + " \"awsRegion\": \"us-east-1\",\n" + " \"userIdentity\":{\n" + " \"type\":\"Service\",\n" - + " \"principalId\":\"dynamodb.amazonaws.com\"\n" + " },\n" + " \"dynamodb\": {\n" - + " \"ApproximateCreationDateTime\": 1.684934517E9,\n" + " \"Keys\": {\n" - + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + " \"key\": {\n" - + " \"S\": \"binary\"\n" + " }\n" + " },\n" + " \"NewImage\": {\n" - + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + " \"asdf1\": {\n" - + " \"B\": \"AAEqQQ==\"\n" + " },\n" + " \"asdf2\": {\n" - + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"QSoBAA==\"\n" - + " ]\n" + " },\n" + " \"key\": {\n" + " \"S\": \"binary\"\n" - + " }\n" + " },\n" + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" + " },\n" - + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " },\n" + " {\n" + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e42f274ee\",\n" - + " \"eventName\": \"INSERT\",\n" + " \"eventVersion\": \"1.1\",\n" - + " \"eventSource\": \"aws:dynamodb\",\n" + " \"awsRegion\": \"us-east-1\",\n" - + " \"dynamodb\": {\n" + " \"ApproximateCreationDateTime\": 1480642020,\n" - + " \"Keys\": {\n" + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" - + " \"key\": {\n" + " \"S\": \"binary\"\n" + " }\n" + " },\n" - + " \"NewImage\": {\n" + " \"val\": {\n" + " \"S\": \"data\"\n" - + " },\n" + " \"asdf1\": {\n" + " \"B\": \"AAEqQQ==\"\n" + " },\n" - + " \"b2\": {\n" + " \"B\": \"test\"\n" + " },\n" + " \"asdf2\": {\n" - + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"QSoBAA==\",\n" - + " \"AAEqQQ==\"\n" + " ]\n" + " },\n" + " \"key\": {\n" - + " \"S\": \"binary\"\n" + " },\n" + " \"Binary\": {\n" - + " \"B\": \"AAEqQQ==\"\n" + " },\n" + " \"Boolean\": {\n" - + " \"BOOL\": true\n" + " },\n" + " \"BinarySet\": {\n" - + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"AAEqQQ==\"\n" - + " ]\n" + " },\n" + " \"List\": {\n" + " \"L\": [\n" - + " {\n" + " \"S\": \"Cookies\"\n" + " },\n" + " {\n" - + " \"S\": \"Coffee\"\n" + " },\n" + " {\n" - + " \"N\": \"3.14159\"\n" + " }\n" + " ]\n" + " },\n" - + " \"Map\": {\n" + " \"M\": {\n" + " \"Name\": {\n" - + " \"S\": \"Joe\"\n" + " },\n" + " \"Age\": {\n" - + " \"N\": \"35\"\n" + " }\n" + " }\n" + " },\n" - + " \"FloatNumber\": {\n" + " \"N\": \"123.45\"\n" + " },\n" - + " \"IntegerNumber\": {\n" + " \"N\": \"123\"\n" + " },\n" - + " \"NumberSet\": {\n" + " \"NS\": [\n" + " \"1234\",\n" - + " \"567.8\"\n" + " ]\n" + " },\n" + " \"Null\": {\n" - + " \"NULL\": true\n" + " },\n" + " \"String\": {\n" - + " \"S\": \"Hello\"\n" + " },\n" + " \"StringSet\": {\n" - + " \"SS\": [\n" + " \"Giraffe\",\n" + " \"Zebra\"\n" - + " ]\n" + " },\n" + " \"EmptyStringSet\": {\n" + " \"SS\": []\n" - + " }\n" + " },\n" + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" + " },\n" - + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " }\n" + " ]\n" + "}"; - - String sampleLBEvent = "{\n" + " \"requestContext\": {\n" + " \"elb\": {\n" - + " \"targetGroupArn\": \"arn:aws:elasticloadbalancing:us-east-1:XXXXXXXXXXX:targetgroup/sample/6d0ecf831eec9f09\"\n" - + " }\n" + " },\n" + " \"httpMethod\": \"GET\",\n" + " \"path\": \"/\",\n" - + " \"queryStringParameters\": {},\n" + " \"headers\": {\n" - + " \"accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n" - + " \"accept-encoding\": \"gzip\",\n" + " \"accept-language\": \"en-US,en;q=0.5\",\n" - + " \"connection\": \"keep-alive\",\n" + " \"cookie\": \"name=value\",\n" - + " \"host\": \"lambda-YYYYYYYY.elb.amazonaws.com\",\n" + " \"upgrade-insecure-requests\": \"1\",\n" - + " \"user-agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:60.0) Gecko/20100101 Firefox/60.0\",\n" - + " \"x-amzn-trace-id\": \"Root=1-5bdb40ca-556d8b0c50dc66f0511bf520\",\n" - + " \"x-forwarded-for\": \"192.0.2.1\",\n" + " \"x-forwarded-port\": \"80\",\n" - + " \"x-forwarded-proto\": \"http\"\n" + " },\n" + " \"body\": \"Hello from ELB\",\n" - + " \"isBase64Encoded\": false\n" + "}"; - - String sampleSQSEvent = "{\n" + " \"Records\": [\n" + " {\n" - + " \"messageId\": \"19dd0b57-b21e-4ac1-bd88-01bbb068cb78\",\n" - + " \"receiptHandle\": \"MessageReceiptHandle\",\n" + " \"body\": \"Hello from SQS!\",\n" - + " \"attributes\": {\n" + " \"ApproximateReceiveCount\": \"1\",\n" - + " \"SentTimestamp\": \"1523232000000\",\n" + " \"SenderId\": \"123456789012\",\n" - + " \"ApproximateFirstReceiveTimestamp\": \"1523232000001\"\n" + " },\n" - + " \"messageAttributes\": {},\n" + " \"md5OfBody\": \"7b270e59b47ff90a553787216d55d91d\",\n" - + " \"eventSource\": \"aws:sqs\",\n" - + " \"eventSourceARN\": \"arn:aws:sqs:eu-central-1:123456789012:MyQueue\",\n" - + " \"awsRegion\": \"eu-central-1\"\n" + " }\n" + " ]\n" + "}"; - - String sampleSNSEvent = "{\n" + " \"Records\": [\n" + " {\n" + " \"EventVersion\": \"1.0\",\n" - + " \"EventSubscriptionArn\": \"arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" - + " \"EventSource\": \"aws:sns\",\n" + " \"Sns\": {\n" + " \"SignatureVersion\": \"1\",\n" - + " \"Timestamp\": \"2019-01-02T12:45:07.000Z\",\n" - + " \"Signature\": \"tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==\",\n" - + " \"SigningCertUrl\": \"https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem\",\n" - + " \"MessageId\": \"95df01b4-ee98-5cb9-9903-4c221d41eb5e\",\n" - + " \"Message\": \"Hello from SNS!\",\n" + " \"MessageAttributes\": {\n" - + " \"Test\": {\n" + " \"Type\": \"String\",\n" - + " \"Value\": \"TestString\"\n" + " },\n" + " \"TestBinary\": {\n" - + " \"Type\": \"Binary\",\n" + " \"Value\": \"TestBinary\"\n" + " }\n" - + " },\n" + " \"Type\": \"Notification\",\n" - + " \"UnsubscribeUrl\": \"https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" - + " \"TopicArn\":\"arn:aws:sns:us-east-2:123456789012:sns-lambda\",\n" - + " \"Subject\": \"TestInvoke\"\n" + " }\n" + " }\n" + " ]\n" + "}"; - - String sampleKinesisEvent = "{" + " \"Records\": [" + " {" + " \"kinesis\": {" - + " \"kinesisSchemaVersion\": \"1.0\"," + " \"partitionKey\": \"1\"," - + " \"sequenceNumber\": \"49590338271490256608559692538361571095921575989136588898\"," - + " \"data\": \"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==\"," - + " \"approximateArrivalTimestamp\": 1545084650.987" + " }," - + " \"eventSource\": \"aws:kinesis\"," + " \"eventVersion\": \"1.0\"," - + " \"eventID\": \"shardId-000000000006:49590338271490256608559692538361571095921575989136588898\"," - + " \"eventName\": \"aws:kinesis:record\"," - + " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\"," - + " \"awsRegion\": \"us-east-2\"," - + " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"" - + " }," + " {" + " \"kinesis\": {" - + " \"kinesisSchemaVersion\": \"1.0\"," + " \"partitionKey\": \"1\"," - + " \"sequenceNumber\": \"49590338271490256608559692540925702759324208523137515618\"," - + " \"data\": \"VGhpcyBpcyBvbmx5IGEgdGVzdC4=\"," - + " \"approximateArrivalTimestamp\": 1545084711.166" + " }," - + " \"eventSource\": \"aws:kinesis\"," + " \"eventVersion\": \"1.0\"," - + " \"eventID\": \"shardId-000000000006:49590338271490256608559692540925702759324208523137515618\"," - + " \"eventName\": \"aws:kinesis:record\"," - + " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\"," - + " \"awsRegion\": \"us-east-2\"," - + " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"" - + " }" + " ]" + "}"; + String someEvent = """ + { + "payload": { + "headers": { + "businessUnit": "1" + } + }, + "headers": { + "aws-context": { + "memoryLimit": 1024, + "awsRequestId": "87a211bf-540f-4f9f-a218-d096a0099999", + "functionName": "myfunction", + "functionVersion": "278", + "invokedFunctionArn": "arn:aws:lambda:us-east-1:xxxxxxx:function:xxxxx:snapstart", + "deadlineTimeInMs": 1712717704761, + "logger": { + "logFiltering": { + "minimumLogLevel": "UNDEFINED" + }, + "logFormatter": {}, + "logFormat": "TEXT" + } + }, + "businessUnit": "1", + "id": "xxxx", + "aws-event": true, + "timestamp": 1712716805129 + } + }"""; + + String scheduleEvent = """ + { + "version": "0", + "id": "17793124-05d4-b198-2fde-7ededc63b103", + "detail-type": "Object Created", + "source": "aws.s3", + "account": "111122223333", + "time": "2021-11-12T00:00:00Z", + "region": "ca-central-1", + "resources": [ + "arn:aws:s3:::amzn-s3-demo-bucket1" + ], + "detail": { + "version": "0", + "bucket": { + "name": "amzn-s3-demo-bucket1" + }, + "object": { + "key": "example-key", + "size": 5, + "etag": "b1946ac92492d2347c6235b4d2611184", + "version-id": "IYV3p45BT0ac8hjHg1houSdS1a.Mro8e", + "sequencer": "617f08299329d189" + }, + "request-id": "N4N7GDK58NMKJ12R", + "requester": "123456789012", + "source-ip-address": "1.2.3.4", + "reason": "PutObject" + } + }"""; + + String dynamoDbEvent = """ + { + "Records": [ + { + "eventID": "f07f8ca4b0b26cb9c4e5e77e69f274ee", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "us-east-1", + "userIdentity":{ + "type":"Service", + "principalId":"dynamodb.amazonaws.com" + }, + "dynamodb": { + "ApproximateCreationDateTime": 1.684934517E9, + "Keys": { + "val": { + "S": "data" + }, + "key": { + "S": "binary" + } + }, + "NewImage": { + "val": { + "S": "data" + }, + "asdf1": { + "B": "AAEqQQ==" + }, + "asdf2": { + "BS": [ + "AAEqQQ==", + "QSoBAA==" + ] + }, + "key": { + "S": "binary" + } + }, + "SequenceNumber": "1405400000000002063282832", + "SizeBytes": 54, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000" + }, + { + "eventID": "f07f8ca4b0b26cb9c4e5e77e42f274ee", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "us-east-1", + "dynamodb": { + "ApproximateCreationDateTime": 1480642020, + "Keys": { + "val": { + "S": "data" + }, + "key": { + "S": "binary" + } + }, + "NewImage": { + "val": { + "S": "data" + }, + "asdf1": { + "B": "AAEqQQ==" + }, + "b2": { + "B": "test" + }, + "asdf2": { + "BS": [ + "AAEqQQ==", + "QSoBAA==", + "AAEqQQ==" + ] + }, + "key": { + "S": "binary" + }, + "Binary": { + "B": "AAEqQQ==" + }, + "Boolean": { + "BOOL": true + }, + "BinarySet": { + "BS": [ + "AAEqQQ==", + "AAEqQQ==" + ] + }, + "List": { + "L": [ + { + "S": "Cookies" + }, + { + "S": "Coffee" + }, + { + "N": "3.14159" + } + ] + }, + "Map": { + "M": { + "Name": { + "S": "Joe" + }, + "Age": { + "N": "35" + } + } + }, + "FloatNumber": { + "N": "123.45" + }, + "IntegerNumber": { + "N": "123" + }, + "NumberSet": { + "NS": [ + "1234", + "567.8" + ] + }, + "Null": { + "NULL": true + }, + "String": { + "S": "Hello" + }, + "StringSet": { + "SS": [ + "Giraffe", + "Zebra" + ] + }, + "EmptyStringSet": { + "SS": [] + } + }, + "SequenceNumber": "1405400000000002063282832", + "SizeBytes": 54, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000" + } + ] + }"""; + + String sampleLBEvent = """ + { + "requestContext": { + "elb": { + "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:XXXXXXXXXXX:targetgroup/sample/6d0ecf831eec9f09" + } + }, + "httpMethod": "GET", + "path": "/", + "queryStringParameters": {}, + "headers": { + "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "accept-encoding": "gzip", + "accept-language": "en-US,en;q=0.5", + "connection": "keep-alive", + "cookie": "name=value", + "host": "lambda-YYYYYYYY.elb.amazonaws.com", + "upgrade-insecure-requests": "1", + "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:60.0) Gecko/20100101 Firefox/60.0", + "x-amzn-trace-id": "Root=1-5bdb40ca-556d8b0c50dc66f0511bf520", + "x-forwarded-for": "192.0.2.1", + "x-forwarded-port": "80", + "x-forwarded-proto": "http" + }, + "body": "Hello from ELB", + "isBase64Encoded": false + }"""; + + String sampleSQSEvent = """ + { + "Records": [ + { + "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", + "receiptHandle": "MessageReceiptHandle", + "body": "Hello from SQS!", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1523232000000", + "SenderId": "123456789012", + "ApproximateFirstReceiveTimestamp": "1523232000001" + }, + "messageAttributes": {}, + "md5OfBody": "7b270e59b47ff90a553787216d55d91d", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:MyQueue", + "awsRegion": "eu-central-1" + } + ] + }"""; + + String sampleSNSEvent = """ + { + "Records": [ + { + "EventVersion": "1.0", + "EventSubscriptionArn": "arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486", + "EventSource": "aws:sns", + "Sns": { + "SignatureVersion": "1", + "Timestamp": "2019-01-02T12:45:07.000Z", + "Signature": "tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==", + "SigningCertUrl": "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem", + "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "Message": "Hello from SNS!", + "MessageAttributes": { + "Test": { + "Type": "String", + "Value": "TestString" + }, + "TestBinary": { + "Type": "Binary", + "Value": "TestBinary" + } + }, + "Type": "Notification", + "UnsubscribeUrl": "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486", + "TopicArn":"arn:aws:sns:us-east-2:123456789012:sns-lambda", + "Subject": "TestInvoke" + } + } + ] + }"""; + + String sampleKinesisEvent = """ + { + "Records": [ + { + "kinesis": { + "kinesisSchemaVersion": "1.0", + "partitionKey": "1", + "sequenceNumber": "49590338271490256608559692538361571095921575989136588898", + "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==", + "approximateArrivalTimestamp": 1545084650.987 + }, + "eventSource": "aws:kinesis", + "eventVersion": "1.0", + "eventID": "shardId-000000000006:49590338271490256608559692538361571095921575989136588898", + "eventName": "aws:kinesis:record", + "invokeIdentityArn": "arn:aws:iam::123456789012:role/lambda-role", + "awsRegion": "us-east-2", + "eventSourceARN": "arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream" + }, + { + "kinesis": { + "kinesisSchemaVersion": "1.0", + "partitionKey": "1", + "sequenceNumber": "49590338271490256608559692540925702759324208523137515618", + "data": "VGhpcyBpcyBvbmx5IGEgdGVzdC4=", + "approximateArrivalTimestamp": 1545084711.166 + }, + "eventSource": "aws:kinesis", + "eventVersion": "1.0", + "eventID": "shardId-000000000006:49590338271490256608559692540925702759324208523137515618", + "eventName": "aws:kinesis:record", + "invokeIdentityArn": "arn:aws:iam::123456789012:role/lambda-role", + "awsRegion": "us-east-2", + "eventSourceARN": "arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream" + } + ] + }"""; // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html - String apiGatewayV2Event = "{\n" + " \"version\": \"2.0\",\n" + " \"routeKey\": \"$default\",\n" - + " \"rawPath\": \"/my/path\",\n" - + " \"rawQueryString\": \"parameter1=value1¶meter1=value2¶meter2=value\",\n" + " \"cookies\": [\n" - + " \"cookie1\",\n" + " \"cookie2\"\n" + " ],\n" + " \"headers\": {\n" - + " \"header1\": \"value1\",\n" + " \"header2\": \"value1,value2\"\n" + " },\n" - + " \"queryStringParameters\": {\n" + " \"parameter1\": \"value1,value2\",\n" - + " \"parameter2\": \"value\"\n" + " },\n" + " \"requestContext\": {\n" - + " \"accountId\": \"123456789012\",\n" + " \"apiId\": \"api-id\",\n" + " \"authentication\": {\n" - + " \"clientCert\": {\n" + " \"clientCertPem\": \"CERT_CONTENT\",\n" - + " \"subjectDN\": \"www.example.com\",\n" + " \"issuerDN\": \"Example issuer\",\n" - + " \"serialNumber\": \"a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1\",\n" - + " \"validity\": {\n" + " \"notBefore\": \"May 28 12:30:02 2019 GMT\",\n" - + " \"notAfter\": \"Aug 5 09:36:04 2021 GMT\"\n" + " }\n" + " }\n" + " },\n" - + " \"authorizer\": {\n" + " \"jwt\": {\n" + " \"claims\": {\n" - + " \"claim1\": \"value1\",\n" + " \"claim2\": \"value2\"\n" + " },\n" - + " \"scopes\": [\n" + " \"scope1\",\n" + " \"scope2\"\n" + " ]\n" - + " }\n" + " },\n" + " \"domainName\": \"id.execute-api.us-east-1.amazonaws.com\",\n" - + " \"domainPrefix\": \"id\",\n" + " \"http\": {\n" + " \"method\": \"POST\",\n" - + " \"path\": \"/my/path\",\n" + " \"protocol\": \"HTTP/1.1\",\n" - + " \"sourceIp\": \"IP\",\n" + " \"userAgent\": \"agent\"\n" + " },\n" - + " \"requestId\": \"id\",\n" + " \"routeKey\": \"$default\",\n" + " \"stage\": \"$default\",\n" - + " \"time\": \"12/Mar/2020:19:03:58 +0000\",\n" + " \"timeEpoch\": 1583348638390\n" + " },\n" - + " \"body\": \"Hello from Lambda\",\n" + " \"pathParameters\": {\n" + " \"parameter1\": \"value1\"\n" - + " },\n" + " \"isBase64Encoded\": false,\n" + " \"stageVariables\": {\n" - + " \"stageVariable1\": \"value1\",\n" + " \"stageVariable2\": \"value2\"\n" + " }\n" + "}"; - - String apiGatewayEvent = "{\n" + " \"resource\": \"/uppercase2\",\n" + " \"path\": \"/uppercase2\",\n" - + " \"httpMethod\": \"POST\",\n" + " \"headers\": {\n" + " \"accept\": \"*/*\",\n" - + " \"content-type\": \"application/json\",\n" - + " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" - + " \"User-Agent\": \"curl/7.54.0\",\n" - + " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" - + " \"X-Forwarded-For\": \"90.37.8.133\",\n" + " \"X-Forwarded-Port\": \"443\",\n" - + " \"X-Forwarded-Proto\": \"https\"\n" + " },\n" + " \"multiValueHeaders\": {\n" - + " \"accept\": [\n" + " \"*/*\"\n" + " ],\n" + " \"content-type\": [\n" - + " \"application/json\"\n" + " ],\n" + " \"Host\": [\n" - + " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + " ],\n" - + " \"User-Agent\": [\n" + " \"curl/7.54.0\"\n" + " ],\n" - + " \"X-Amzn-Trace-Id\": [\n" + " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" - + " ],\n" + " \"X-Forwarded-For\": [\n" + " \"90.37.8.133\"\n" + " ],\n" - + " \"X-Forwarded-Port\": [\n" + " \"443\"\n" + " ],\n" - + " \"X-Forwarded-Proto\": [\n" + " \"https\"\n" + " ]\n" + " },\n" - + " \"queryStringParameters\": null,\n" + " \"multiValueQueryStringParameters\": null,\n" - + " \"pathParameters\": null,\n" + " \"stageVariables\": null,\n" + " \"requestContext\": {\n" - + " \"resourceId\": \"qf0io6\",\n" + " \"resourcePath\": \"/uppercase2\",\n" - + " \"httpMethod\": \"POST\",\n" + " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" - + " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + " \"path\": \"/test/uppercase2\",\n" - + " \"accountId\": \"123456789098\",\n" + " \"protocol\": \"HTTP/1.1\",\n" - + " \"stage\": \"test\",\n" + " \"domainPrefix\": \"fhul32ccy2\",\n" - + " \"requestTimeEpoch\": 1590571934872,\n" - + " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + " \"identity\": {\n" - + " \"cognitoIdentityPoolId\": null,\n" + " \"accountId\": null,\n" - + " \"cognitoIdentityId\": null,\n" + " \"caller\": null,\n" - + " \"sourceIp\": \"90.37.8.133\",\n" + " \"principalOrgId\": null,\n" - + " \"accessKey\": null,\n" + " \"cognitoAuthenticationType\": null,\n" - + " \"cognitoAuthenticationProvider\": null,\n" + " \"userArn\": null,\n" - + " \"userAgent\": \"curl/7.54.0\",\n" + " \"user\": null\n" + " },\n" - + " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" - + " \"apiId\": \"fhul32ccy2\"\n" + " },\n" + " \"body\":\"hello\",\n" - + " \"isBase64Encoded\": false\n" + "}"; - - String s3Event = "{\n" + " \"Records\": [\n" + " {\n" + " \"eventVersion\": \"2.1\",\n" - + " \"eventSource\": \"aws:s3\",\n" + " \"awsRegion\": \"eu-central-1\",\n" - + " \"eventTime\": \"2023-11-04T23:44:23.905Z\",\n" - + " \"eventName\": \"ObjectCreated:Put\",\n" + " \"userIdentity\": {\n" - + " \"principalId\": \"AWS:xxxxxxxxxxxxxxxxxxx\"\n" + " },\n" - + " \"requestParameters\": {\n" + " \"sourceIPAddress\": \"x.x.x.x\"\n" - + " },\n" + " \"responseElements\": {\n" - + " \"x-amz-request-id\": \"xxxxxxxxxxxxxxxx\",\n" - + " \"x-amz-id-2\": \"xxxxxxxxxxxxxxxxxxxx\"\n" + " },\n" - + " \"s3\": {\n" + " \"s3SchemaVersion\": \"1.0\",\n" - + " \"configurationId\": \"xxxxxxxxxxxxxxxxxxxxxxxx\",\n" + " \"bucket\": {\n" - + " \"name\": \"xxxxxxxxxxxxxxx\",\n" + " \"ownerIdentity\": {\n" - + " \"principalId\": \"xxxxxxxxxxxxxxxxxx\"\n" + " },\n" - + " \"arn\": \"arn:aws:s3:::xxxxxxxxxxxxxxxxx\"\n" + " },\n" - + " \"object\": {\n" + " \"key\": \"xxxxxxxxxxxxxxxx\",\n" - + " \"size\": 6064,\n" + " \"eTag\": \"xxxxxxxxxxxxx\",\n" - + " \"sequencer\": \"xxxxxxxxxxxxxx\"\n" + " }\n" + " }\n" - + " }\n" + " ]\n" + "}"; - - String apiGatewayEventWithStructuredBody = "{\n" + " \"resource\": \"/uppercase2\",\n" - + " \"path\": \"/uppercase2\",\n" + " \"httpMethod\": \"POST\",\n" + " \"headers\": {\n" - + " \"accept\": \"*/*\",\n" + " \"content-type\": \"application/json\",\n" - + " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" - + " \"User-Agent\": \"curl/7.54.0\",\n" - + " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" - + " \"X-Forwarded-For\": \"90.37.8.133\",\n" + " \"X-Forwarded-Port\": \"443\",\n" - + " \"X-Forwarded-Proto\": \"https\"\n" + " },\n" + " \"multiValueHeaders\": {\n" - + " \"accept\": [\n" + " \"*/*\"\n" + " ],\n" + " \"content-type\": [\n" - + " \"application/json\"\n" + " ],\n" + " \"Host\": [\n" - + " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + " ],\n" - + " \"User-Agent\": [\n" + " \"curl/7.54.0\"\n" + " ],\n" - + " \"X-Amzn-Trace-Id\": [\n" + " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" - + " ],\n" + " \"X-Forwarded-For\": [\n" + " \"90.37.8.133\"\n" + " ],\n" - + " \"X-Forwarded-Port\": [\n" + " \"443\"\n" + " ],\n" - + " \"X-Forwarded-Proto\": [\n" + " \"https\"\n" + " ]\n" + " },\n" - + " \"queryStringParameters\": null,\n" + " \"multiValueQueryStringParameters\": null,\n" - + " \"pathParameters\": null,\n" + " \"stageVariables\": null,\n" + " \"requestContext\": {\n" - + " \"resourceId\": \"qf0io6\",\n" + " \"resourcePath\": \"/uppercase2\",\n" - + " \"httpMethod\": \"POST\",\n" + " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" - + " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + " \"path\": \"/test/uppercase2\",\n" - + " \"accountId\": \"123456789098\",\n" + " \"protocol\": \"HTTP/1.1\",\n" - + " \"stage\": \"test\",\n" + " \"domainPrefix\": \"fhul32ccy2\",\n" - + " \"requestTimeEpoch\": 1590571934872,\n" - + " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + " \"identity\": {\n" - + " \"cognitoIdentityPoolId\": null,\n" + " \"accountId\": null,\n" - + " \"cognitoIdentityId\": null,\n" + " \"caller\": null,\n" - + " \"sourceIp\": \"90.37.8.133\",\n" + " \"principalOrgId\": null,\n" - + " \"accessKey\": null,\n" + " \"cognitoAuthenticationType\": null,\n" - + " \"cognitoAuthenticationProvider\": null,\n" + " \"userArn\": null,\n" - + " \"userAgent\": \"curl/7.54.0\",\n" + " \"user\": null\n" + " },\n" - + " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" - + " \"apiId\": \"fhul32ccy2\"\n" + " },\n" + " \"body\":{\"name\":\"Jim Lahey\"},\n" - + " \"isBase64Encoded\": false\n" + "}"; - - String apiGatewayEventWithArray = "{\n" + " \"resource\": \"/uppercase2\",\n" - + " \"path\": \"/uppercase2\",\n" + " \"httpMethod\": \"POST\",\n" + " \"headers\": {\n" - + " \"accept\": \"*/*\",\n" + " \"content-type\": \"application/json\",\n" - + " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" - + " \"User-Agent\": \"curl/7.54.0\",\n" - + " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" - + " \"X-Forwarded-For\": \"90.37.8.133\",\n" + " \"X-Forwarded-Port\": \"443\",\n" - + " \"X-Forwarded-Proto\": \"https\"\n" + " },\n" + " \"multiValueHeaders\": {\n" - + " \"accept\": [\n" + " \"*/*\"\n" + " ],\n" + " \"content-type\": [\n" - + " \"application/json\"\n" + " ],\n" + " \"Host\": [\n" - + " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + " ],\n" - + " \"User-Agent\": [\n" + " \"curl/7.54.0\"\n" + " ],\n" - + " \"X-Amzn-Trace-Id\": [\n" + " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" - + " ],\n" + " \"X-Forwarded-For\": [\n" + " \"90.37.8.133\"\n" + " ],\n" - + " \"X-Forwarded-Port\": [\n" + " \"443\"\n" + " ],\n" - + " \"X-Forwarded-Proto\": [\n" + " \"https\"\n" + " ]\n" + " },\n" - + " \"queryStringParameters\": null,\n" + " \"multiValueQueryStringParameters\": null,\n" - + " \"pathParameters\": null,\n" + " \"stageVariables\": null,\n" + " \"requestContext\": {\n" - + " \"resourceId\": \"qf0io6\",\n" + " \"resourcePath\": \"/uppercase2\",\n" - + " \"httpMethod\": \"POST\",\n" + " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" - + " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + " \"path\": \"/test/uppercase2\",\n" - + " \"accountId\": \"123456789098\",\n" + " \"protocol\": \"HTTP/1.1\",\n" - + " \"stage\": \"test\",\n" + " \"domainPrefix\": \"fhul32ccy2\",\n" - + " \"requestTimeEpoch\": 1590571934872,\n" - + " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + " \"identity\": {\n" - + " \"cognitoIdentityPoolId\": null,\n" + " \"accountId\": null,\n" - + " \"cognitoIdentityId\": null,\n" + " \"caller\": null,\n" - + " \"sourceIp\": \"90.37.8.133\",\n" + " \"principalOrgId\": null,\n" - + " \"accessKey\": null,\n" + " \"cognitoAuthenticationType\": null,\n" - + " \"cognitoAuthenticationProvider\": null,\n" + " \"userArn\": null,\n" - + " \"userAgent\": \"curl/7.54.0\",\n" + " \"user\": null\n" + " },\n" - + " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" - + " \"apiId\": \"fhul32ccy2\"\n" + " },\n" - + " \"body\":[{\"name\":\"Jim Lahey\"},{\"name\":\"Ricky\"}],\n" + " \"isBase64Encoded\": false\n" - + "}"; - - String gwAuthorizerEvent = "{\n" + " \"type\":\"TOKEN\",\n" + " \"authorizationToken\":\"allow\",\n" - + " \"methodArn\":\"arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/\"\n" + "}"; + String apiGatewayV2Event = """ + { + "version": "2.0", + "routeKey": "$default", + "rawPath": "/my/path", + "rawQueryString": "parameter1=value1¶meter1=value2¶meter2=value", + "cookies": [ + "cookie1", + "cookie2" + ], + "headers": { + "header1": "value1", + "header2": "value1,value2" + }, + "queryStringParameters": { + "parameter1": "value1,value2", + "parameter2": "value" + }, + "requestContext": { + "accountId": "123456789012", + "apiId": "api-id", + "authentication": { + "clientCert": { + "clientCertPem": "CERT_CONTENT", + "subjectDN": "www.example.com", + "issuerDN": "Example issuer", + "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", + "validity": { + "notBefore": "May 28 12:30:02 2019 GMT", + "notAfter": "Aug 5 09:36:04 2021 GMT" + } + } + }, + "authorizer": { + "jwt": { + "claims": { + "claim1": "value1", + "claim2": "value2" + }, + "scopes": [ + "scope1", + "scope2" + ] + } + }, + "domainName": "id.execute-api.us-east-1.amazonaws.com", + "domainPrefix": "id", + "http": { + "method": "POST", + "path": "/my/path", + "protocol": "HTTP/1.1", + "sourceIp": "IP", + "userAgent": "agent" + }, + "requestId": "id", + "routeKey": "$default", + "stage": "$default", + "time": "12/Mar/2020:19:03:58 +0000", + "timeEpoch": 1583348638390 + }, + "body": "Hello from Lambda", + "pathParameters": { + "parameter1": "value1" + }, + "isBase64Encoded": false, + "stageVariables": { + "stageVariable1": "value1", + "stageVariable2": "value2" + } + }"""; + + String apiGatewayEvent = """ + { + "resource": "/uppercase2", + "path": "/uppercase2", + "httpMethod": "POST", + "headers": { + "accept": "*/*", + "content-type": "application/json", + "Host": "fhul32ccy2.execute-api.eu-west-3.amazonaws.com", + "User-Agent": "curl/7.54.0", + "X-Amzn-Trace-Id": "Root=1-5ece339e-e0595766066d703ec70f1522", + "X-Forwarded-For": "90.37.8.133", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "accept": [ + "*/*" + ], + "content-type": [ + "application/json" + ], + "Host": [ + "fhul32ccy2.execute-api.eu-west-3.amazonaws.com" + ], + "User-Agent": [ + "curl/7.54.0" + ], + "X-Amzn-Trace-Id": [ + "Root=1-5ece339e-e0595766066d703ec70f1522" + ], + "X-Forwarded-For": [ + "90.37.8.133" + ], + "X-Forwarded-Port": [ + "443" + ], + "X-Forwarded-Proto": [ + "https" + ] + }, + "queryStringParameters": null, + "multiValueQueryStringParameters": null, + "pathParameters": null, + "stageVariables": null, + "requestContext": { + "resourceId": "qf0io6", + "resourcePath": "/uppercase2", + "httpMethod": "POST", + "extendedRequestId": "NL0A1EokCGYFZOA=", + "requestTime": "27/May/2020:09:32:14 +0000", + "path": "/test/uppercase2", + "accountId": "123456789098", + "protocol": "HTTP/1.1", + "stage": "test", + "domainPrefix": "fhul32ccy2", + "requestTimeEpoch": 1590571934872, + "requestId": "b96500aa-f92a-43c3-9360-868ba4053a00", + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "sourceIp": "90.37.8.133", + "principalOrgId": null, + "accessKey": null, + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "curl/7.54.0", + "user": null + }, + "domainName": "fhul32ccy2.execute-api.eu-west-3.amazonaws.com", + "apiId": "fhul32ccy2" + }, + "body":"hello", + "isBase64Encoded": false + }"""; + + String s3Event = """ + { + "Records": [ + { + "eventVersion": "2.1", + "eventSource": "aws:s3", + "awsRegion": "eu-central-1", + "eventTime": "2023-11-04T23:44:23.905Z", + "eventName": "ObjectCreated:Put", + "userIdentity": { + "principalId": "AWS:xxxxxxxxxxxxxxxxxxx" + }, + "requestParameters": { + "sourceIPAddress": "x.x.x.x" + }, + "responseElements": { + "x-amz-request-id": "xxxxxxxxxxxxxxxx", + "x-amz-id-2": "xxxxxxxxxxxxxxxxxxxx" + }, + "s3": { + "s3SchemaVersion": "1.0", + "configurationId": "xxxxxxxxxxxxxxxxxxxxxxxx", + "bucket": { + "name": "xxxxxxxxxxxxxxx", + "ownerIdentity": { + "principalId": "xxxxxxxxxxxxxxxxxx" + }, + "arn": "arn:aws:s3:::xxxxxxxxxxxxxxxxx" + }, + "object": { + "key": "xxxxxxxxxxxxxxxx", + "size": 6064, + "eTag": "xxxxxxxxxxxxx", + "sequencer": "xxxxxxxxxxxxxx" + } + } + } + ] + }"""; + + String apiGatewayEventWithStructuredBody = """ + { + "resource": "/uppercase2", + "path": "/uppercase2", + "httpMethod": "POST", + "headers": { + "accept": "*/*", + "content-type": "application/json", + "Host": "fhul32ccy2.execute-api.eu-west-3.amazonaws.com", + "User-Agent": "curl/7.54.0", + "X-Amzn-Trace-Id": "Root=1-5ece339e-e0595766066d703ec70f1522", + "X-Forwarded-For": "90.37.8.133", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "accept": [ + "*/*" + ], + "content-type": [ + "application/json" + ], + "Host": [ + "fhul32ccy2.execute-api.eu-west-3.amazonaws.com" + ], + "User-Agent": [ + "curl/7.54.0" + ], + "X-Amzn-Trace-Id": [ + "Root=1-5ece339e-e0595766066d703ec70f1522" + ], + "X-Forwarded-For": [ + "90.37.8.133" + ], + "X-Forwarded-Port": [ + "443" + ], + "X-Forwarded-Proto": [ + "https" + ] + }, + "queryStringParameters": null, + "multiValueQueryStringParameters": null, + "pathParameters": null, + "stageVariables": null, + "requestContext": { + "resourceId": "qf0io6", + "resourcePath": "/uppercase2", + "httpMethod": "POST", + "extendedRequestId": "NL0A1EokCGYFZOA=", + "requestTime": "27/May/2020:09:32:14 +0000", + "path": "/test/uppercase2", + "accountId": "123456789098", + "protocol": "HTTP/1.1", + "stage": "test", + "domainPrefix": "fhul32ccy2", + "requestTimeEpoch": 1590571934872, + "requestId": "b96500aa-f92a-43c3-9360-868ba4053a00", + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "sourceIp": "90.37.8.133", + "principalOrgId": null, + "accessKey": null, + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "curl/7.54.0", + "user": null + }, + "domainName": "fhul32ccy2.execute-api.eu-west-3.amazonaws.com", + "apiId": "fhul32ccy2" + }, + "body":{"name":"Jim Lahey"}, + "isBase64Encoded": false + }"""; + + String apiGatewayEventWithArray = """ + { + "resource": "/uppercase2", + "path": "/uppercase2", + "httpMethod": "POST", + "headers": { + "accept": "*/*", + "content-type": "application/json", + "Host": "fhul32ccy2.execute-api.eu-west-3.amazonaws.com", + "User-Agent": "curl/7.54.0", + "X-Amzn-Trace-Id": "Root=1-5ece339e-e0595766066d703ec70f1522", + "X-Forwarded-For": "90.37.8.133", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "accept": [ + "*/*" + ], + "content-type": [ + "application/json" + ], + "Host": [ + "fhul32ccy2.execute-api.eu-west-3.amazonaws.com" + ], + "User-Agent": [ + "curl/7.54.0" + ], + "X-Amzn-Trace-Id": [ + "Root=1-5ece339e-e0595766066d703ec70f1522" + ], + "X-Forwarded-For": [ + "90.37.8.133" + ], + "X-Forwarded-Port": [ + "443" + ], + "X-Forwarded-Proto": [ + "https" + ] + }, + "queryStringParameters": null, + "multiValueQueryStringParameters": null, + "pathParameters": null, + "stageVariables": null, + "requestContext": { + "resourceId": "qf0io6", + "resourcePath": "/uppercase2", + "httpMethod": "POST", + "extendedRequestId": "NL0A1EokCGYFZOA=", + "requestTime": "27/May/2020:09:32:14 +0000", + "path": "/test/uppercase2", + "accountId": "123456789098", + "protocol": "HTTP/1.1", + "stage": "test", + "domainPrefix": "fhul32ccy2", + "requestTimeEpoch": 1590571934872, + "requestId": "b96500aa-f92a-43c3-9360-868ba4053a00", + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "sourceIp": "90.37.8.133", + "principalOrgId": null, + "accessKey": null, + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "curl/7.54.0", + "user": null + }, + "domainName": "fhul32ccy2.execute-api.eu-west-3.amazonaws.com", + "apiId": "fhul32ccy2" + }, + "body":[{"name":"Jim Lahey"},{"name":"Ricky"}], + "isBase64Encoded": false + }"""; + + String gwAuthorizerEvent = """ + { + "type":"TOKEN", + "authorizationToken":"allow", + "methodArn":"arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/" + }"""; @BeforeEach public void before() throws Exception { 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 f3dbf216f..0cc9f37ed 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 @@ -146,8 +146,13 @@ public void errorUnexpectedWhitelabel() throws Exception { @Test public void validatePostWithBody() throws Exception { ServerlessHttpServletRequest request = new ServerlessHttpServletRequest(null, "POST", "/pets/"); - String jsonPet = "{\n" + " \"id\":\"1234\",\n" + " \"breed\":\"Canish\",\n" + " \"name\":\"Foo\",\n" - + " \"date\":\"2012-04-23T18:25:43.511Z\"\n" + "}"; + String jsonPet = """ + { + "id":"1234", + "breed":"Canish", + "name":"Foo", + "date":"2012-04-23T18:25:43.511Z" + }"""; request.setContent(jsonPet.getBytes()); request.setContentType("application/json"); ServerlessHttpServletResponse response = new ServerlessHttpServletResponse(); @@ -177,8 +182,13 @@ public void validatePostWithoutBody() throws Exception { public void validatePostAsyncWithBody() throws Exception { // System.setProperty("spring.main.banner-mode", "off"); ServerlessHttpServletRequest request = new ServerlessHttpServletRequest(null, "POST", "/petsAsync/"); - String jsonPet = "{\n" + " \"id\":\"1234\",\n" + " \"breed\":\"Canish\",\n" + " \"name\":\"Foo\",\n" - + " \"date\":\"2012-04-23T18:25:43.511Z\"\n" + "}"; + String jsonPet = """ + { + "id":"1234", + "breed":"Canish", + "name":"Foo", + "date":"2012-04-23T18:25:43.511Z" + }"""; request.setContent(jsonPet.getBytes()); request.setContentType("application/json"); ServerlessHttpServletResponse response = new ServerlessHttpServletResponse(); diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java index 06d04846c..de42b1a3a 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java @@ -83,9 +83,12 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderImperative() { String id = UUID.randomUUID().toString(); - String payload = "{\n" + " \"version\" : \"1.0\",\n" - + " \"releaseName\" : \"Spring Framework\",\n" + " \"releaseDate\" : \"24-03-2004\"\n" - + " }"; + String payload = """ + { + "version" : "1.0", + "releaseName" : "Spring Framework", + "releaseDate" : "24-03-2004" + }"""; Message inputMessage = CloudEventMessageBuilder.withData(payload) .setId(id) @@ -117,9 +120,12 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderReactive() { String id = UUID.randomUUID().toString(); - String payload = "{\n" + " \"version\" : \"1.0\",\n" - + " \"releaseName\" : \"Spring Framework\",\n" + " \"releaseDate\" : \"24-03-2004\"\n" - + " }"; + String payload = """ + { + "version" : "1.0", + "releaseName" : "Spring Framework", + "releaseDate" : "24-03-2004" + }"""; Message inputMessage = CloudEventMessageBuilder.withData(payload) .setId(id) @@ -151,9 +157,12 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderReactiveMono() { String id = UUID.randomUUID().toString(); - String payload = "{\n" + " \"version\" : \"1.0\",\n" - + " \"releaseName\" : \"Spring Framework\",\n" + " \"releaseDate\" : \"24-03-2004\"\n" - + " }"; + String payload = """ + { + "version" : "1.0", + "releaseName" : "Spring Framework", + "releaseDate" : "24-03-2004" + }"""; Message inputMessage = CloudEventMessageBuilder.withData(payload) .setId(id) @@ -210,11 +219,19 @@ public void testBinaryPojoToPojoDefaultOutputHeaderProviderWithPrefix() { @SuppressWarnings("unchecked") @Test public void testStructuredPojoToPojoDefaultOutputAttributeProvider() throws Exception { - String payload = "{\n" + " \"specversion\" : \"1.0\",\n" + " \"type\" : \"org.springframework\",\n" - + " \"source\" : \"https://spring.io/\",\n" + " \"id\" : \"A234-1234-1234\",\n" - + " \"datacontenttype\" : \"application/json\",\n" + " \"data\" : {\n" - + " \"version\" : \"1.0\",\n" + " \"releaseName\" : \"Spring Framework\",\n" - + " \"releaseDate\" : \"24-03-2004\"\n" + " }\n" + "}"; + String payload = """ + { + "specversion" : "1.0", + "type" : "org.springframework", + "source" : "https://spring.io/", + "id" : "A234-1234-1234", + "datacontenttype" : "application/json", + "data" : { + "version" : "1.0", + "releaseName" : "Spring Framework", + "releaseDate" : "24-03-2004" + } + }"""; Function function = this.lookup("springRelease", TestConfiguration.class); Message inputMessage = MessageBuilder.withPayload(payload) @@ -240,11 +257,19 @@ public void testStructuredPojoToPojoDefaultOutputAttributeProvider() throws Exce @SuppressWarnings("unchecked") @Test public void testStructuredPojoToPojoMessageFunction() throws Exception { - String payload = "{\n" + " \"specversion\" : \"1.0\",\n" + " \"type\" : \"org.springframework\",\n" - + " \"source\" : \"https://spring.io/\",\n" + " \"id\" : \"A234-1234-1234\",\n" - + " \"datacontenttype\" : \"application/json\",\n" + " \"data\" : {\n" - + " \"version\" : \"1.0\",\n" + " \"releaseName\" : \"Spring Framework\",\n" - + " \"releaseDate\" : \"24-03-2004\"\n" + " }\n" + "}"; + String payload = """ + { + "specversion" : "1.0", + "type" : "org.springframework", + "source" : "https://spring.io/", + "id" : "A234-1234-1234", + "datacontenttype" : "application/json", + "data" : { + "version" : "1.0", + "releaseName" : "Spring Framework", + "releaseDate" : "24-03-2004" + } + }"""; Function function = this.lookup("springReleaseAsMessage", TestConfiguration.class); Message inputMessage = MessageBuilder.withPayload(payload) @@ -271,11 +296,18 @@ public void testStructuredPojoToPojoMessageFunction() throws Exception { @SuppressWarnings("unchecked") @Test public void testStructuredPojoToPojoDefaultOutputAttributeProviderNoDataContentType() throws Exception { - String payload = "{\n" + " \"ce_specversion\" : \"1.0\",\n" + " \"ce_type\" : \"org.springframework\",\n" - + " \"ce_source\" : \"https://spring.io/\",\n" + " \"ce_id\" : \"A234-1234-1234\",\n" - + " \"ce_data\" : {\n" + " \"version\" : \"1.0\",\n" - + " \"releaseName\" : \"Spring Framework\",\n" + " \"releaseDate\" : \"24-03-2004\"\n" - + " }\n" + "}"; + String payload = """ + { + "ce_specversion" : "1.0", + "ce_type" : "org.springframework", + "ce_source" : "https://spring.io/", + "ce_id" : "A234-1234-1234", + "ce_data" : { + "version" : "1.0", + "releaseName" : "Spring Framework", + "releaseDate" : "24-03-2004" + } + }"""; Function function = this.lookup("springRelease", TestConfiguration.class); Message inputMessage = MessageBuilder.withPayload(payload) diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMaskerTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMaskerTests.java index df8bd4c54..600db2f0b 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMaskerTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMaskerTests.java @@ -36,55 +36,159 @@ public class JsonMaskerTests { - private String event = "{\n" + " \"Records\": [\n" + " {\n" - + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e69f274ee\",\n" + " \"eventName\": \"INSERT\",\n" - + " \"eventVersion\": \"1.1\",\n" + " \"eventSource\": \"aws:dynamodb\",\n" - + " \"awsRegion\": \"us-east-1\",\n" + " \"userIdentity\":{\n" + " \"type\":\"Service\",\n" - + " \"principalId\":\"dynamodb.amazonaws.com\"\n" + " },\n" + " \"dynamodb\": {\n" - + " \"ApproximateCreationDateTime\": 1.684934517E9,\n" + " \"Keys\": {\n" - + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + " \"key\": {\n" - + " \"S\": \"binary\"\n" + " }\n" + " },\n" + " \"NewImage\": {\n" - + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" + " \"asdf1\": {\n" - + " \"B\": \"AAEqQQ==\"\n" + " },\n" + " \"asdf2\": {\n" - + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"QSoBAA==\"\n" - + " ]\n" + " },\n" + " \"key\": {\n" + " \"S\": \"binary\"\n" - + " }\n" + " },\n" + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" + " },\n" - + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " },\n" + " {\n" + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e42f274ee\",\n" - + " \"eventName\": \"INSERT\",\n" + " \"eventVersion\": \"1.1\",\n" - + " \"eventSource\": \"aws:dynamodb\",\n" + " \"awsRegion\": \"us-east-1\",\n" - + " \"dynamodb\": {\n" + " \"ApproximateCreationDateTime\": 1480642020,\n" - + " \"Keys\": {\n" + " \"val\": {\n" + " \"S\": \"data\"\n" + " },\n" - + " \"key\": {\n" + " \"S\": \"binary\"\n" + " }\n" + " },\n" - + " \"NewImage\": {\n" + " \"val\": {\n" + " \"S\": \"data\"\n" - + " },\n" + " \"asdf1\": {\n" + " \"B\": \"AAEqQQ==\"\n" + " },\n" - + " \"b2\": {\n" + " \"B\": \"test\"\n" + " },\n" + " \"asdf2\": {\n" - + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"QSoBAA==\",\n" - + " \"AAEqQQ==\"\n" + " ]\n" + " },\n" + " \"key\": {\n" - + " \"S\": \"binary\"\n" + " },\n" + " \"Binary\": {\n" - + " \"B\": \"AAEqQQ==\"\n" + " },\n" + " \"Boolean\": {\n" - + " \"BOOL\": true\n" + " },\n" + " \"BinarySet\": {\n" - + " \"BS\": [\n" + " \"AAEqQQ==\",\n" + " \"AAEqQQ==\"\n" - + " ]\n" + " },\n" + " \"List\": {\n" + " \"L\": [\n" - + " {\n" + " \"S\": \"Cookies\"\n" + " },\n" + " {\n" - + " \"S\": \"Coffee\"\n" + " },\n" + " {\n" - + " \"N\": \"3.14159\"\n" + " }\n" + " ]\n" + " },\n" - + " \"Map\": {\n" + " \"M\": {\n" + " \"Name\": {\n" - + " \"S\": \"Joe\"\n" + " },\n" + " \"Age\": {\n" - + " \"N\": \"35\"\n" + " }\n" + " }\n" + " },\n" - + " \"FloatNumber\": {\n" + " \"N\": \"123.45\"\n" + " },\n" - + " \"IntegerNumber\": {\n" + " \"N\": \"123\"\n" + " },\n" - + " \"NumberSet\": {\n" + " \"NS\": [\n" + " \"1234\",\n" - + " \"567.8\"\n" + " ]\n" + " },\n" + " \"Null\": {\n" - + " \"NULL\": true\n" + " },\n" + " \"String\": {\n" - + " \"S\": \"Hello\"\n" + " },\n" + " \"StringSet\": {\n" - + " \"SS\": [\n" + " \"Giraffe\",\n" + " \"Zebra\"\n" - + " ]\n" + " },\n" + " \"EmptyStringSet\": {\n" + " \"SS\": []\n" - + " }\n" + " },\n" + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" + " },\n" - + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " }\n" + " ]\n" + "}"; + private String event = """ + { + "Records": [ + { + "eventID": "f07f8ca4b0b26cb9c4e5e77e69f274ee", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "us-east-1", + "userIdentity":{ + "type":"Service", + "principalId":"dynamodb.amazonaws.com" + }, + "dynamodb": { + "ApproximateCreationDateTime": 1.684934517E9, + "Keys": { + "val": { + "S": "data" + }, + "key": { + "S": "binary" + } + }, + "NewImage": { + "val": { + "S": "data" + }, + "asdf1": { + "B": "AAEqQQ==" + }, + "asdf2": { + "BS": [ + "AAEqQQ==", + "QSoBAA==" + ] + }, + "key": { + "S": "binary" + } + }, + "SequenceNumber": "1405400000000002063282832", + "SizeBytes": 54, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000" + }, + { + "eventID": "f07f8ca4b0b26cb9c4e5e77e42f274ee", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "us-east-1", + "dynamodb": { + "ApproximateCreationDateTime": 1480642020, + "Keys": { + "val": { + "S": "data" + }, + "key": { + "S": "binary" + } + }, + "NewImage": { + "val": { + "S": "data" + }, + "asdf1": { + "B": "AAEqQQ==" + }, + "b2": { + "B": "test" + }, + "asdf2": { + "BS": [ + "AAEqQQ==", + "QSoBAA==", + "AAEqQQ==" + ] + }, + "key": { + "S": "binary" + }, + "Binary": { + "B": "AAEqQQ==" + }, + "Boolean": { + "BOOL": true + }, + "BinarySet": { + "BS": [ + "AAEqQQ==", + "AAEqQQ==" + ] + }, + "List": { + "L": [ + { + "S": "Cookies" + }, + { + "S": "Coffee" + }, + { + "N": "3.14159" + } + ] + }, + "Map": { + "M": { + "Name": { + "S": "Joe" + }, + "Age": { + "N": "35" + } + } + }, + "FloatNumber": { + "N": "123.45" + }, + "IntegerNumber": { + "N": "123" + }, + "NumberSet": { + "NS": [ + "1234", + "567.8" + ] + }, + "Null": { + "NULL": true + }, + "String": { + "S": "Hello" + }, + "StringSet": { + "SS": [ + "Giraffe", + "Zebra" + ] + }, + "EmptyStringSet": { + "SS": [] + } + }, + "SequenceNumber": "1405400000000002063282832", + "SizeBytes": 54, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000" + } + ] + }"""; private List maskedKeys = new ArrayList<>(); diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml index f0be38384..931a88a6c 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -3,4 +3,9 @@ "-//Puppy Crawl//DTD Suppressions 1.1//EN" "https://www.puppycrawl.com/dtds/suppressions_1_1.dtd"> + + + + + From c703fa799be4d74ba3549be80e217b397cbf0918 Mon Sep 17 00:00:00 2001 From: Hyunwoo Jung Date: Thu, 20 Nov 2025 00:32:55 +0900 Subject: [PATCH 3/3] Polish some comments Signed-off-by: Hyunwoo Jung --- .../function/adapter/gcp/FunctionInvoker.java | 8 +++--- .../serverless/web/RequestResponseTests.java | 12 ++++----- .../cloudevent/CloudEventMessageUtils.java | 4 +-- .../context/FunctionRegistration.java | 4 +-- .../context/catalog/FunctionTypeUtils.java | 5 ++-- .../context/config/RoutingFunction.java | 22 +++++++++++----- .../SmartCompositeMessageConverter.java | 26 +++++-------------- .../cloudevent/CloudEventFunctionTests.java | 20 +++++++------- ...CloudEventMessageUtilsAndBuilderTests.java | 6 ++--- ...BeanFactoryAwareFunctionRegistryTests.java | 14 +++------- .../catalog/SimpleFunctionRegistryTests.java | 25 +++++++----------- 11 files changed, 62 insertions(+), 84 deletions(-) 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 6f1dc6cb6..858fc7e58 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 @@ -211,13 +211,11 @@ else if (result.getHeaders().containsKey("Content-Type")) { } } - /* - * This methd get the result from reactor's publisher. + /** + * This method gets the result from reactor's publisher. * * For reference: - * https://github.com/spring-cloud/spring-cloud-function/blob/main/spring-cloud- - * function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/ - * springframework/cloud/function/adapter/aws/AWSLambdaUtils.java + * https://github.com/spring-cloud/spring-cloud-function/blob/main/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java */ private Message getResultFromPublisher(Object resultObject) { List results = new ArrayList<>(); 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 0cc9f37ed..5ca51f3c0 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 @@ -147,12 +147,12 @@ public void errorUnexpectedWhitelabel() throws Exception { public void validatePostWithBody() throws Exception { ServerlessHttpServletRequest request = new ServerlessHttpServletRequest(null, "POST", "/pets/"); String jsonPet = """ - { - "id":"1234", - "breed":"Canish", - "name":"Foo", - "date":"2012-04-23T18:25:43.511Z" - }"""; + { + "id":"1234", + "breed":"Canish", + "name":"Foo", + "date":"2012-04-23T18:25:43.511Z" + }"""; request.setContent(jsonPet.getBytes()); request.setContentType("application/json"); ServerlessHttpServletResponse response = new ServerlessHttpServletResponse(); 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 41fe83f5a..418db3e64 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 @@ -56,8 +56,8 @@ public final class CloudEventMessageUtils { @Override public MimeType resolve(@Nullable MessageHeaders headers) { - if (headers.containsKey("content-type")) { // this is temporary workaround for - // RSocket + if (headers.containsKey("content-type")) { + // this is temporary workaround for RSocket return MimeType.valueOf(headers.get("content-type").toString()); } return super.resolve(headers); diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java index 3977ec026..acdf6cf64 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java @@ -121,8 +121,8 @@ public FunctionRegistration type(Type type) { return this; } Type discoveredFunctionType = type; // FunctionTypeUtils.discoverFunctionTypeFromClass(this.target.getClass()); - if (discoveredFunctionType == null) { // only valid for Kafka Stream KStream[] - // return type. + if (discoveredFunctionType == null) { + // only valid for Kafka Stream KStream[] return type. return null; } 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 b3cff0355..b0fc10b6c 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 @@ -453,9 +453,8 @@ public static Type discoverFunctionType(Object function, String functionName, else if (function instanceof FunctionRegistration) { return ((FunctionRegistration) function).getType(); } - if (applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX)) { // for - // Kotlin - // primarily + if (applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX)) { + // for Kotlin primarily FunctionRegistration fr = applicationContext .getBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, FunctionRegistration.class); return fr.getType(); 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 dfad78983..6a0415161 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 @@ -114,13 +114,21 @@ public Object apply(Object input) { /* * - Check if `this.routingCallback` is present and if it is use it (only for Message - * input) If NOT - Check if spring.cloud.function.definition is set in header and if - * it is use it.(only for Message input) If NOT - Check if - * spring.cloud.function.routing-expression is set in header and if it is set use it - * (only for Message input) If NOT - Check `spring.cloud.function.definition` is set - * in FunctionProperties and if it is use it (Message and Publisher) If NOT - Check - * `spring.cloud.function.routing-expression` is set in FunctionProperties and if it - * is use it (Message and Publisher) If NOT - Fail + * input) + * + * If NOT - Check if spring.cloud.function.definition is set in header and if it is + * use it.(only for Message input) + * + * If NOT - Check if spring.cloud.function.routing-expression is set in header and if + * it is set use it (only for Message input) + * + * If NOT - Check `spring.cloud.function.definition` is set in FunctionProperties and + * if it is use it (Message and Publisher) + * + * If NOT - Check `spring.cloud.function.routing-expression` is set in + * FunctionProperties and if it is use it (Message and Publisher) + * + * If NOT - Fail */ private Object route(Object input, boolean originalInputIsPublisher) { FunctionInvocationWrapper function = null; 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 9c729a0ea..33c049de8 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 @@ -113,19 +113,10 @@ public Object fromMessage(Message message, Class targetClass, @Nullable Ob for (Iterator iterator = getConverters().iterator(); iterator.hasNext() && !isConverted;) { MessageConverter converter = (MessageConverter) iterator.next(); - if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { // TODO - // Stream - // stuff, - // needs - // to - // be - // removed - Message m = MessageBuilder.withPayload(item).copyHeaders(message.getHeaders()).build(); // TODO - // Message - // creating - // may - // be - // expensive + // TODO Stream stuff, needs to be removed + if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { + // TODO Message creating may be expensive + Message m = MessageBuilder.withPayload(item).copyHeaders(message.getHeaders()).build(); Object conversionResult = (converter instanceof SmartMessageConverter & genericItemRawType != genericItemType ? ((SmartMessageConverter) converter).fromMessage(m, genericItemRawType, @@ -146,13 +137,8 @@ public Object fromMessage(Message message, Class targetClass, @Nullable Ob } else { for (MessageConverter converter : getConverters()) { - if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { // TODO - // Stream - // stuff, - // needs - // to - // be - // removed + // TODO Stream stuff, needs to be removed + if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { result = (converter instanceof SmartMessageConverter ? ((SmartMessageConverter) converter).fromMessage(message, targetClass, conversionHint) : converter.fromMessage(message, targetClass)); diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java index de42b1a3a..6d13456f2 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java @@ -244,11 +244,11 @@ public void testStructuredPojoToPojoDefaultOutputAttributeProvider() throws Exce assertThat(resultMessage.getPayload().getReleaseDate()) .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); assertThat(resultMessage.getPayload().getVersion()).isEqualTo("2.0"); - // /* - // * Validates that although user only deals with POJO, the framework recognizes - // * both on input and output that it is dealing with Cloud Event and generates - // * appropriate headers/attributes - // */ + /* + * Validates that although user only deals with POJO, the framework recognizes + * both on input and output that it is dealing with Cloud Event and generates + * appropriate headers/attributes + */ assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(SpringReleaseEvent.class.getName()); assertThat(CloudEventMessageUtils.getSource(resultMessage)).isEqualTo(URI.create("http://spring.io/")); @@ -282,11 +282,11 @@ public void testStructuredPojoToPojoMessageFunction() throws Exception { assertThat(resultMessage.getPayload().getReleaseDate()) .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); assertThat(resultMessage.getPayload().getVersion()).isEqualTo("2.0"); - // /* - // * Validates that although user only deals with POJO, the framework recognizes - // * both on input and output that it is dealing with Cloud Event and generates - // * appropriate headers/attributes - // */ + /* + * Validates that although user only deals with POJO, the framework recognizes + * both on input and output that it is dealing with Cloud Event and generates + * appropriate headers/attributes + */ assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(SpringReleaseEvent.class.getName()); assertThat(CloudEventMessageUtils.getSource(resultMessage)) diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java index 447e76ce8..773b1d597 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java @@ -80,9 +80,9 @@ public void testAttributeRecognitionAndCanonicalization() { Message kafkaMessage = CloudEventMessageBuilder.fromMessage(httpMessage) .build(CloudEventMessageUtils.KAFKA_ATTR_PREFIX); attributes = CloudEventMessageUtils.getAttributes(kafkaMessage); - assertThat(attributes.size()).isEqualTo(4); // id will be auto injected, so always - // at least 4 (as tehre are 4 required - // attributes in CE) + // id will be auto injected, so always at least 4 (as there are 4 required + // attributes in CE) + assertThat(attributes.size()).isEqualTo(4); assertThat(kafkaMessage.getHeaders().get("ce_source")).isNotNull(); assertThat(CloudEventMessageUtils.getSource(kafkaMessage)).isEqualTo(URI.create("https://foo.bar")); assertThat(kafkaMessage.getHeaders().get("ce_type")).isNotNull(); 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 a61928edb..21b4184f7 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 @@ -314,8 +314,8 @@ public void testImperativeFunction() { } @Test - public void testConsumerFunction() { // function that returns Void, effectively a - // Consumer + public void testConsumerFunction() { + // function that returns Void, effectively a Consumer FunctionCatalog catalog = this.configureCatalog(); Function consumerFunction = catalog.lookup("consumerFunction"); @@ -1436,14 +1436,8 @@ public static class SCF_GH_768ConfigurationAsFunction { public Function, String> echoToString() { return data -> { for (Entry dataEntry : data.entrySet()) { - assertThat(dataEntry.getValue()).isInstanceOf(Date.class); // would - // fail if - // value - // would - // not be - // converted - // to - // Person + // would fail if value would not be converted to Person + assertThat(dataEntry.getValue()).isInstanceOf(Date.class); } return data.toString(); }; 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 8350bc249..7583a6ca0 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 @@ -108,16 +108,11 @@ public void before() { } @ParameterizedTest - @ValueSource(strings = { "aaaaaaaaaa", // no problem - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]" // protobuf - // encoder - // prepends - // '[' - // for - // length - // (91 - // bytes) - }) + @ValueSource(strings = { + // no problem + "aaaaaaaaaa", + // protobuf encoder prepends '[' for length (91 bytes) + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]" }) public void testSCF1094(String stringValue) throws IOException { Function getValue = msg -> msg != null ? msg.getValue() : null; @@ -202,10 +197,8 @@ public void testSCF768() { Function, String> function = persons -> { for (Entry entry : persons.entrySet()) { - assertThat(entry.getValue().getName()).isNotEmpty(); // would fail if - // value would not - // be converted to - // Person + // would fail if value would not be converted to Person + assertThat(entry.getValue().getName()).isNotEmpty(); } return persons.toString(); }; @@ -365,8 +358,8 @@ public void testFunctionLookup() { // FunctionInvocationWrapper lookedUpFunction = catalog.lookup("hello"); FunctionInvocationWrapper lookedUpFunction = catalog.lookup("hello"); - assertThat(lookedUpFunction).isNotNull(); // because we only have one and can look - // it up with any name + // because we only have one and can look it up with any name + assertThat(lookedUpFunction).isNotNull(); FunctionRegistration registration2 = new FunctionRegistration<>(function, "foo2") .type(TestFunction.class); catalog.register(registration2);