Azure / azure-functions-java-worker

Java runtime and core types for Microsoft Azure Functions
MIT License
87 stars 53 forks source link

Add support for LocalDate (or OffsetDateTime, etc.) in HttpRequestMessage<Optional<T>> in Functions with Spring Cloud #709

Open noopliez opened 1 year ago

noopliez commented 1 year ago

Investigative information

Please provide the following:

Repro steps

Provide the steps required to reproduce the problem:

  1. Define any POJO with a LocalDate or OffsetDateTime property, e.g. User
  2. Use the POJO as request body for your HTTP call, e.g. HttpRequestMessage<Optional<User>>
  3. Build & run your Application (mvn azure-functions:run)
  4. Call your function and define the request body as defined in the POJO

Expected behavior

Provide a description of the expected behavior.

After Step 4 I expect to have called the function successfully

Actual behavior

Provide a description of the actual behavior observed.

Step 4 won't work because I always get the same error message as follows:

[2023-03-16T10:26:35.390Z] [WARNING] {CheckedBiFunction.tryApply}: InaccessibleObjectException: Unable to make field private final int java.time.LocalDate.year accessible: module java.base does not "opens java.time" to unnamed module @2b05039f
[2023-03-16T10:26:35.447Z] Executed 'Functions.person' (Failed, Id=c4d45e9c-b0df-49ab-9051-004d3e8a384b, Duration=288ms)
[2023-03-16T10:26:35.450Z] System.Private.CoreLib: Exception while executing function: Functions.person. System.Private.CoreLib: Result: Failure
Exception: ClassCastException: Cannot convert com.microsoft.azure.functions.worker.binding.RpcHttpRequestDataSource@3a19100ato type com.microsoft.azure.functions.HttpRequestMessage<java.util.Optional<com.example.models.User>>
Stack: java.lang.ClassCastException: Cannot convert com.microsoft.azure.functions.worker.binding.RpcHttpRequestDataSource@3a19100ato type com.microsoft.azure.functions.HttpRequestMessage<java.util.Optional<com.example.models.User>>
[2023-03-16T10:26:35.455Z]      at com.microsoft.azure.functions.worker.binding.DataOperations.generalAssignment(DataOperations.java:191)
[2023-03-16T10:26:35.460Z]      at com.microsoft.azure.functions.worker.binding.DataOperations.apply(DataOperations.java:120)
[2023-03-16T10:26:35.462Z]      at com.microsoft.azure.functions.worker.binding.DataSource.computeByType(DataSource.java:56)
[2023-03-16T10:26:35.470Z]      at com.microsoft.azure.functions.worker.binding.RpcHttpRequestDataSource.computeByType(RpcHttpRequestDataSource.java:20)
[2023-03-16T10:26:35.477Z]      at com.microsoft.azure.functions.worker.binding.DataSource.computeByName(DataSource.java:42)
[2023-03-16T10:26:35.479Z]      at com.microsoft.azure.functions.worker.binding.RpcHttpRequestDataSource.computeByName(RpcHttpRequestDataSource.java:20)
[2023-03-16T10:26:35.496Z]      at com.microsoft.azure.functions.worker.binding.BindingDataStore.getDataByName(BindingDataStore.java:59)
[2023-03-16T10:26:35.505Z]      at com.microsoft.azure.functions.worker.binding.ExecutionContextDataSource.getBindingData(ExecutionContextDataSource.java:176)
[2023-03-16T10:26:35.511Z]      at com.microsoft.azure.functions.worker.broker.ParameterResolver.resolve(ParameterResolver.java:44)
[2023-03-16T10:26:35.518Z]      at com.microsoft.azure.functions.worker.broker.ParameterResolver.resolveArguments(ParameterResolver.java:22)
[2023-03-16T10:26:35.525Z]      at com.microsoft.azure.functions.worker.broker.EnhancedJavaMethodExecutorImpl.execute(EnhancedJavaMethodExecutorImpl.java:20)
[2023-03-16T10:26:35.528Z]      at com.microsoft.azure.functions.worker.chain.FunctionExecutionMiddleware.invoke(FunctionExecutionMiddleware.java:19)
[2023-03-16T10:26:35.552Z]      at com.microsoft.azure.functions.worker.chain.InvocationChain.doNext(InvocationChain.java:21)
[2023-03-16T10:26:35.561Z]      at com.microsoft.azure.functions.worker.broker.JavaFunctionBroker.invokeMethod(JavaFunctionBroker.java:125)
[2023-03-16T10:26:35.566Z]      at com.microsoft.azure.functions.worker.handler.InvocationRequestHandler.execute(InvocationRequestHandler.java:37)
[2023-03-16T10:26:35.572Z]      at com.microsoft.azure.functions.worker.handler.InvocationRequestHandler.execute(InvocationRequestHandler.java:12)
[2023-03-16T10:26:35.580Z]      at com.microsoft.azure.functions.worker.handler.MessageHandler.handle(MessageHandler.java:44)
[2023-03-16T10:26:35.587Z]      at com.microsoft.azure.functions.worker.JavaWorkerClient$StreamingMessagePeer.lambda$onNext$0(JavaWorkerClient.java:93)
[2023-03-16T10:26:35.589Z]      at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
[2023-03-16T10:26:35.603Z]      at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[2023-03-16T10:26:35.615Z]      at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
[2023-03-16T10:26:35.627Z]      at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
[2023-03-16T10:26:35.629Z]      at java.base/java.lang.Thread.run(Thread.java:833)

Known workarounds

Provide a description of any known workarounds.

The current workaround would be to use a String instead of the immutable date objects. But that would not be suffice, because in our project we want to work with generated models that uses the ISO datetime.

Another workaround would be that we use Java.util.Date when generating our models, but I don't really like the idea of using that.

Related information

Provide any related information

Source ```java @Component @RequiredArgsConstructor public class PersonHandler { private final Person person; @FunctionName("person") public HttpResponseMessage execute( @HttpTrigger( name = "request", methods = {HttpMethod.POST, HttpMethod.DELETE}, authLevel = AuthorizationLevel.ANONYMOUS ) HttpRequestMessage> request, @EventHubOutput( ) OutputBinding output, ExecutionContext context) { context.getLogger().info("Hello there, General Kenobi"); final User user= request.getBody().orElse(null); if (user == null) { return request .createResponseBuilder(HttpStatus.BAD_REQUEST) .body("Please do it right") .build(); } else { final CloudEvent event = person.apply(user); final byte[] serializedEvent = Objects.requireNonNull(EventFormatProvider .getInstance() .resolveFormat(JsonFormat.CONTENT_TYPE)) .serialize(event); output.setValue(serializedEvent); return request .createResponseBuilder(HttpStatus.OK) .body("Sucess!") .build(); } } } @Component @RequiredArgsConstructor public class Person implements Function { @Override public CloudEvent apply(User user) { try { final ObjectMapper objectMapper = new ObjectMapper(); final byte[] serializedPojo = objectMapper.writeValueAsBytes(user); final LocalDateTime now = LocalDateTime.now(); final ZoneId zone = ZoneId.of("Europe/Berlin"); final ZoneOffset zoneOffset = zone.getRules().getOffset(now); return CloudEventBuilder.v1() .withId(UUID.randomUUID().toString()) .withSource(URI.create("/some/uri")) .withType("TYPE") .withDataContentType("application/json") .withSubject("SUBJECT") .withTime(OffsetDateTime.of(now, zoneOffset)) .withData(serializedPojo) .build(); } catch (JsonProcessingException e) { return null; } } } 4.0.0 com.example beep 0.0.1-${revision} com.example.beep org.springframework.boot spring-boot-starter-parent 3.0.1 BeepFunction local 17 UTF-8 17 17 1.24.0 3.0.0 4.0.0 2.4.2 1.18.26 not relevant not relevant not relevant westeurope EP1 ${project.build.directory}/azure-functions/${functionAppName} com.example.BeepApplication org.springframework.cloud spring-cloud-function-adapter-azure org.springframework.cloud spring-cloud-starter-function-webflux provided org.projectlombok lombok ${lombok.version} provided io.cloudevents cloudevents-core ${cloudevents.version} io.cloudevents cloudevents-json-jackson ${cloudevents.version} org.openapitools jackson-databind-nullable 0.2.4 io.swagger.core.v3 swagger-annotations 2.2.8 jakarta.validation jakarta.validation-api 3.0.2 jakarta.annotation jakarta.annotation-api 2.1.1 org.springframework.boot spring-boot-starter-test test org.junit.jupiter junit-jupiter-api test org.mockito mockito-core 2.23.4 test org.assertj assertj-core 3.24.2 test org.springframework.cloud spring-cloud-function-dependencies ${spring.cloud.function.dependencies} pom import com.microsoft.azure.functions azure-functions-java-library ${azure.functions.java.library.version} com.microsoft.azure azure-functions-maven-plugin ${azure.functions.maven.plugin.version} org.apache.maven.plugins maven-resources-plugin 3.3.0 org.apache.maven.plugins maven-dependency-plugin 3.4.0 org.apache.maven.plugins maven-clean-plugin org.openapitools openapi-generator-maven-plugin 6.3.0 generate-contract generate ${project.basedir}/src/main/resources/some-spec.yaml spring true true true @lombok.Builder @lombok.NoArgsConstructor @lombok.AllArgsConstructor false false com.microsoft.azure azure-functions-maven-plugin ${functionResourceGroup} ${functionAppName} ${functionAppRegion} ${functionAppServicePlanName} ${functionAppPricingTier} ${project.basedir}/src/main/azure/host.json ${project.basedir}/src/main/azure/local.settings.json linux 17 FUNCTIONS_EXTENSION_VERSION ~4 package-functions package maven-clean-plugin 3.2.0 obj org.springframework.boot spring-boot-maven-plugin ```
CrawX commented 1 year ago

Could this be related to #424?

noopliez commented 1 year ago

@CrawX I'm not sure if it fits for our Problem, but it sure would be nice to configure the serializer.