diff --git a/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs b/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs index be60e9ea..43557cf5 100644 --- a/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs +++ b/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs @@ -25,6 +25,7 @@ public static class Constants public static string InputBindingBlobContainer = "test-input-java-new"; public static string OutputBindingBlobContainer = "test-output-java-new"; public static string TriggerInputBindingBlobClientSdk = "test-triggerinput-blobclient"; + public static string TriggerInputBindingBlobInputBlobClientSdk = "test-triggerinput-blobinput-blobclient"; public static string TriggerInputBindingBlobContainerClientSdk = "test-triggerinput-blobcontclient"; // Xunit Fixtures and Collections diff --git a/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/StorageHelpers.cs b/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/StorageHelpers.cs index 5cf85348..04c0bdf9 100644 --- a/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/StorageHelpers.cs +++ b/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/StorageHelpers.cs @@ -85,6 +85,7 @@ public async static Task ClearBlobContainers() await ClearBlobContainer(Constants.InputBindingBlobContainer); await ClearBlobContainer(Constants.OutputBindingBlobContainer); await ClearBlobContainer(Constants.TriggerInputBindingBlobClientSdk); + await ClearBlobContainer(Constants.TriggerInputBindingBlobInputBlobClientSdk); await ClearBlobContainer(Constants.TriggerInputBindingBlobContainerClientSdk); } @@ -94,6 +95,7 @@ public async static Task CreateBlobContainers() await CreateBlobContainer(Constants.InputBindingBlobContainer); await CreateBlobContainer(Constants.OutputBindingBlobContainer); await CreateBlobContainer(Constants.TriggerInputBindingBlobClientSdk); + await CreateBlobContainer(Constants.TriggerInputBindingBlobInputBlobClientSdk); await CreateBlobContainer(Constants.TriggerInputBindingBlobContainerClientSdk); } diff --git a/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/StorageEndToEndTests.cs b/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/StorageEndToEndTests.cs index 676e573b..fe223b8e 100644 --- a/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/StorageEndToEndTests.cs +++ b/emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/StorageEndToEndTests.cs @@ -131,6 +131,26 @@ public async Task BlobTriggerToBlob_BlobClient_Succeeds() Assert.Equal("Hello World", result); } + [Fact] + [Trait("Category", "SdkTypes")] + public async Task BlobTriggerToBlob_FromBlobInput_BlobClient_Succeeds() + { + string fileName = "testfile"; + + //cleanup + await StorageHelpers.ClearBlobContainers(); + + //Setup + await StorageHelpers.CreateBlobContainers(); + + //Trigger + await StorageHelpers.UpdloadFileToContainer(Constants.TriggerInputBindingBlobInputBlobClientSdk, fileName); + + //Verify + string result = await StorageHelpers.DownloadFileFromContainer(Constants.OutputBindingBlobContainer, "testfile"); + Assert.Equal("Hello World", result); + } + [Fact] [Trait("Category", "SdkTypes")] public async Task BlobTriggerToBlob_BlobContainerClient_Succeeds() diff --git a/emulatedtests/pom.xml b/emulatedtests/pom.xml index 6557ce14..66ab05a0 100644 --- a/emulatedtests/pom.xml +++ b/emulatedtests/pom.xml @@ -21,7 +21,7 @@ UTF-8 1.8 - 1.37.1 + 1.38.0 3.1.0 2.1.0 1.0.0-beta.1 diff --git a/emulatedtests/src/main/java/com/microsoft/azure/functions/endtoend/BlobTriggerSdkTypesTests.java b/emulatedtests/src/main/java/com/microsoft/azure/functions/endtoend/BlobTriggerSdkTypesTests.java index 8671ae12..0ac172e4 100644 --- a/emulatedtests/src/main/java/com/microsoft/azure/functions/endtoend/BlobTriggerSdkTypesTests.java +++ b/emulatedtests/src/main/java/com/microsoft/azure/functions/endtoend/BlobTriggerSdkTypesTests.java @@ -18,7 +18,7 @@ public class BlobTriggerSdkTypesTests { @FunctionName("BlobTriggerUsingBlobClientToBlobTest") @StorageAccount("AzureWebJobsStorage") public void BlobTriggerToBlobTest_BlobClient( - @BlobTrigger(name = "triggerBlob", path = "test-triggerinput-blobclient/{name}", dataType = "binary") BlobClient triggerBlobClient, + @BlobTrigger(name = "triggerBlob", path = "test-triggerinput-blobclient/{name}") BlobClient triggerBlobClient, @BindingName("name") String fileName, @BlobOutput(name = "outputBlob", path = "test-output-java-new/testfile.txt", dataType = "binary") OutputBinding outputBlob, final ExecutionContext context @@ -34,13 +34,35 @@ public void BlobTriggerToBlobTest_BlobClient( context.getLogger().info("Uploaded blob " + fileName + " to container test-output-java-new/testfile.txt"); } + /** + * This function will be invoked when a new or updated blob is detected at the specified path. The blob contents are provided as input to this function. + */ + @FunctionName("BlobTriggerUsingBlobInputBlobClientToBlobTest") + @StorageAccount("AzureWebJobsStorage") + public void BlobTriggerBlobInputToBlobTest_BlobClient( + @BlobTrigger(name = "triggerBlob", path = "test-triggerinput-blobinput-blobclient/{name}") BlobClient triggerBlobClient, + @BlobInput(name = "inputBlob", path = "test-triggerinput-blobinput-blobclient/testfile.txt") BlobClient outputBlobClient, + @BlobOutput(name = "outputBlob", path = "test-output-java-new/testfile.txt", dataType = "binary") OutputBinding outputBlob, + final ExecutionContext context + ) { + context.getLogger().info("BlobTriggerUsingBlobInputBlobClientToBlobTest triggered for blob: " + triggerBlobClient.getBlobName()); + + // Download the blob content + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + outputBlobClient.downloadStream(outputStream); + + // Set the downloaded content as output + outputBlob.setValue(outputStream.toByteArray()); + context.getLogger().info("Uploaded blob " + triggerBlobClient.getBlobUrl() + " to container test-output-java-new/testfile.txt"); + } + /** * This function will be invoked when a new or updated blob is detected at the specified path. The blob contents are provided as input to this function. */ @FunctionName("BlobTriggerUsingBlobContainerClientToBlobTest") @StorageAccount("AzureWebJobsStorage") public void BlobTriggerToBlobTest_BlobContainerClient( - @BlobTrigger(name = "triggerBlob", path = "test-triggerinput-blobcontclient/{name}", dataType = "binary") BlobContainerClient triggerBlobContainerClient, + @BlobTrigger(name = "triggerBlob", path = "test-triggerinput-blobcontclient/{name}") BlobContainerClient triggerBlobContainerClient, @BindingName("name") String fileName, @BlobOutput(name = "outputBlob", path = "test-output-java-new/testfile.txt", dataType = "binary") OutputBinding outputBlob, final ExecutionContext context diff --git a/eng/ci/public-build.yml b/eng/ci/public-build.yml index 9d37ce12..1aebca9a 100644 --- a/eng/ci/public-build.yml +++ b/eng/ci/public-build.yml @@ -24,11 +24,6 @@ resources: name: 1ESPipelineTemplates/1ESPipelineTemplates ref: refs/tags/release -parameters: - - name: runSdkTypesTests - type: boolean - default: false - extends: template: v1/1ES.Unofficial.PipelineTemplate.yml@1es parameters: @@ -60,12 +55,10 @@ extends: - template: /eng/ci/templates/jobs/run-emulated-tests-windows.yml@self parameters: poolName: 1es-pool-azfunc-public - runSdkTypesTests: ${{ parameters.runSdkTypesTests }} - stage: TestLinux dependsOn: [] jobs: - template: /eng/ci/templates/jobs/run-emulated-tests-linux.yml@self parameters: - poolName: 1es-pool-azfunc-public - runSdkTypesTests: ${{ parameters.runSdkTypesTests }} + poolName: 1es-pool-azfunc-public \ No newline at end of file diff --git a/eng/ci/templates/jobs/build.yml b/eng/ci/templates/jobs/build.yml index 0716c20c..9d229516 100644 --- a/eng/ci/templates/jobs/build.yml +++ b/eng/ci/templates/jobs/build.yml @@ -14,9 +14,6 @@ jobs: - pwsh: | java -version displayName: 'Check default java version' - - pwsh: | - .\installAdditionsLocally.ps1 - displayName: 'Install java-additions locally' - pwsh: | mvn clean package displayName: 'Build java worker' \ No newline at end of file diff --git a/eng/ci/templates/jobs/run-emulated-tests-linux.yml b/eng/ci/templates/jobs/run-emulated-tests-linux.yml index bb6b5add..93beca1b 100644 --- a/eng/ci/templates/jobs/run-emulated-tests-linux.yml +++ b/eng/ci/templates/jobs/run-emulated-tests-linux.yml @@ -2,9 +2,6 @@ parameters: - name: poolName type: string default: '' - - name: runSdkTypesTests - type: boolean - default: false jobs: - job: "TestLinux" @@ -34,16 +31,16 @@ jobs: JAVA_VERSION: 'microsoft-jdk-11.0.21-linux-x64' JDK_PATH: 'jdk-11.0.21+9' JAVA_VERSION_SPEC: '11' -# microsoft-open-jdk-17-linux: -# JDK_DOWNLOAD_LINK: 'https://aka.ms/download-jdk/microsoft-jdk-17.0.9-linux-x64.tar.gz' -# JAVA_VERSION: 'microsoft-jdk-17.0.9-linux-x64' -# JDK_PATH: 'jdk-17.0.9+8' -# JAVA_VERSION_SPEC: '17' -# microsoft-open-jdk-21-linux: -# JDK_DOWNLOAD_LINK: 'https://aka.ms/download-jdk/microsoft-jdk-21.0.1-linux-x64.tar.gz' -# JAVA_VERSION: 'microsoft-jdk-21.0.1-linux-x64' -# JDK_PATH: 'jdk-21.0.1+12' -# JAVA_VERSION_SPEC: '21' + microsoft-open-jdk-17-linux: + JDK_DOWNLOAD_LINK: 'https://aka.ms/download-jdk/microsoft-jdk-17.0.9-linux-x64.tar.gz' + JAVA_VERSION: 'microsoft-jdk-17.0.9-linux-x64' + JDK_PATH: 'jdk-17.0.9+8' + JAVA_VERSION_SPEC: '17' + microsoft-open-jdk-21-linux: + JDK_DOWNLOAD_LINK: 'https://aka.ms/download-jdk/microsoft-jdk-21.0.1-linux-x64.tar.gz' + JAVA_VERSION: 'microsoft-jdk-21.0.1-linux-x64' + JDK_PATH: 'jdk-21.0.1+12' + JAVA_VERSION_SPEC: '21' steps: - task: NuGetToolInstaller@1 @@ -84,9 +81,6 @@ jobs: docker compose -f emulatedtests/utils/docker-compose.yml pull docker compose -f emulatedtests/utils/docker-compose.yml up -d displayName: 'Install Azurite and Start Emulators' - - pwsh: | - .\installAdditionsLocally.ps1 - displayName: 'Install java-additions locally' - pwsh: | if ("$(isTag)"){ $buildNumber="$(Build.SourceBranchName)" @@ -101,7 +95,7 @@ jobs: displayName: 'Executing build script' - pwsh: | cd ./emulatedtests - mvn clean package -DexcludedClassPattern="**/BlobTriggerSdkTypesTests.java" `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B + mvn clean package `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B Copy-Item "confluent_cloud_cacert.pem" "./target/azure-functions/azure-functions-java-emulatedtests" displayName: 'Package Java for E2E' - pwsh: | @@ -119,54 +113,8 @@ jobs: command: 'test' projects: | emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E.csproj - arguments: '--filter "Category!=SdkTypes"' env: - JAVA_HOME: $(JavaHome) AzureWebJobsStorage: "UseDevelopmentStorage=true" + JAVA_ENABLE_SDK_TYPES: "true" displayName: 'Build & Run tests' - continueOnError: false - # ------------------------------------------ - # Conditionally run an additional set of steps for "SDK types" scenario - # ------------------------------------------ - - ${{ if eq(parameters.runSdkTypesTests, true) }}: - - pwsh: | - .\installMavenPluginLocally.ps1 - displayName: 'Install maven plugin locally' - - pwsh: | - if ("$(isTag)"){ - $buildNumber="$(Build.SourceBranchName)" - Write-Host "Found git tag." - } - else { - $buildNumber="$(Build.BuildNumber)-v4" - Write-Host "git tag not found. Setting package suffix to '$buildNumber'" - } - Write-Host "##vso[task.setvariable variable=buildNumber;isOutput=true;]$buildNumber" - .\package-pipeline.ps1 -buildNumber $buildNumber - displayName: 'Executing build script' - - pwsh: | - cd ./emulatedtests - mvn clean package `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B - Copy-Item "confluent_cloud_cacert.pem" "./target/azure-functions/azure-functions-java-emulatedtests" - displayName: 'Package Java for E2E' - - pwsh: | - .\setup-tests-pipeline.ps1 - displayName: 'Setup test environment -- Install the Core Tools' - - bash: | - chmod +x ./Azure.Functions.Cli/func - chmod +x ./Azure.Functions.Cli/gozip - export PATH=$PATH:./Azure.Functions.Cli - func --version - displayName: 'Setup Core Tools - Linux' - - task: DotNetCoreCLI@2 - retryCountOnTaskFailure: 3 - inputs: - command: 'test' - projects: | - emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E.csproj - env: - JAVA_HOME: $(JavaHome) - AzureWebJobsStorage: "UseDevelopmentStorage=true" - ENABLE_SDK_TYPES: "true" - displayName: 'Build & Run tests' - continueOnError: false \ No newline at end of file + continueOnError: false \ No newline at end of file diff --git a/eng/ci/templates/jobs/run-emulated-tests-windows.yml b/eng/ci/templates/jobs/run-emulated-tests-windows.yml index 0c32775e..5e0dff46 100644 --- a/eng/ci/templates/jobs/run-emulated-tests-windows.yml +++ b/eng/ci/templates/jobs/run-emulated-tests-windows.yml @@ -2,9 +2,6 @@ parameters: - name: poolName type: string default: '' - - name: runSdkTypesTests - type: boolean - default: false jobs: - job: "TestWindows" @@ -28,18 +25,22 @@ jobs: JDK_DOWNLOAD_LINK: 'https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u392-b08/OpenJDK8U-jdk_x64_windows_hotspot_8u392b08.zip' JAVA_VERSION: 'OpenJDK8U-jdk_x64_windows_hotspot_8u392b08' JDK_PATH: 'jdk8u392-b08' + JAVA_VERSION_SPEC: '8' microsoft-open-jdk-11-windows: JDK_DOWNLOAD_LINK: 'https://aka.ms/download-jdk/microsoft-jdk-11.0.21-windows-x64.zip' JAVA_VERSION: 'microsoft-jdk-11.0.21-windows-x64' JDK_PATH: 'jdk-11.0.21+9' + JAVA_VERSION_SPEC: '11' microsoft-open-jdk-17-windows: JDK_DOWNLOAD_LINK: 'https://aka.ms/download-jdk/microsoft-jdk-17.0.9-windows-x64.zip' JAVA_VERSION: 'microsoft-jdk-17.0.9-windows-x64' JDK_PATH: 'jdk-17.0.9+8' + JAVA_VERSION_SPEC: '17' microsoft-open-jdk-21-windows: JDK_DOWNLOAD_LINK: 'https://aka.ms/download-jdk/microsoft-jdk-21.0.1-windows-x64.zip' JAVA_VERSION: 'microsoft-jdk-21.0.1-windows-x64' JDK_PATH: 'jdk-21.0.1+12' + JAVA_VERSION_SPEC: '21' steps: - task: NuGetToolInstaller@1 @@ -56,22 +57,25 @@ jobs: displayName: 'Install .NET 6' inputs: version: 6.0.x - - pwsh: | + - pwsh: | # Download JDK for later installation Invoke-WebRequest $(JDK_DOWNLOAD_LINK) -OutFile "$(JAVA_VERSION).zip" - Expand-Archive -Force "$(JAVA_VERSION).zip" . - cd $(JDK_PATH) $current = get-location | select -ExpandProperty Path - cd .. - Write-Host "##vso[task.setvariable variable=JavaHome;]$current" - displayName: 'Download and setup Java for Windows' + Write-Host "##vso[task.setvariable variable=downloadPath;]$current" + displayName: 'Download jdk for Windows' + - task: JavaToolInstaller@0 # Install JDK downloaded from previous task + inputs: + versionSpec: $(JAVA_VERSION_SPEC) + jdkArchitectureOption: 'x64' + jdkSourceOption: LocalDirectory + jdkFile: "$(downloadPath)/$(JAVA_VERSION).zip" + jdkDestinationDirectory: "$(downloadPath)/externals" + cleanDestinationDirectory: true + displayName: 'Setup Java for Windows' - bash: | npm install -g azurite mkdir azurite azurite --silent --location azurite --debug azurite\debug.log & displayName: 'Install and Run Azurite' - - pwsh: | - .\installAdditionsLocally.ps1 - displayName: 'Install java-additions locally' - pwsh: | if ("$(isTag)"){ $buildNumber="$(Build.SourceBranchName)" @@ -86,7 +90,7 @@ jobs: displayName: 'Executing build script' - pwsh: | cd ./emulatedtests - mvn clean package -DexcludedClassPattern="**/BlobTriggerSdkTypesTests.java" `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B + mvn clean package `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B Copy-Item "confluent_cloud_cacert.pem" "./target/azure-functions/azure-functions-java-emulatedtests" displayName: 'Package Java for E2E' - pwsh: | @@ -103,54 +107,8 @@ jobs: command: 'test' projects: | emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E.csproj - arguments: '--filter "Category!=SdkTypes"' env: - JAVA_HOME: $(JavaHome) AzureWebJobsStorage: "UseDevelopmentStorage=true" - ENABLE_SDK_TYPES: "true" + JAVA_ENABLE_SDK_TYPES: "true" displayName: 'Build & Run tests' - continueOnError: false - # ------------------------------------------ - # Conditionally run an additional set of steps for "SDK types" scenario - # ------------------------------------------ - - ${{ if eq(parameters.runSdkTypesTests, true) }}: - - pwsh: | - .\installMavenPluginLocally.ps1 - displayName: 'Install maven plugin locally' - - pwsh: | - if ("$(isTag)"){ - $buildNumber="$(Build.SourceBranchName)" - Write-Host "Found git tag." - } - else { - $buildNumber="$(Build.BuildNumber)-v4" - Write-Host "git tag not found. Setting package suffix to '$buildNumber'" - } - Write-Host "##vso[task.setvariable variable=buildNumber;isOutput=true;]$buildNumber" - .\package-pipeline.ps1 -buildNumber $buildNumber - displayName: 'Executing build script (SDK types)' - - pwsh: | - cd ./emulatedtests - mvn clean package `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B - Copy-Item "confluent_cloud_cacert.pem" "./target/azure-functions/azure-functions-java-emulatedtests" - displayName: 'Package Java for E2E (SDK types)' - - pwsh: | - .\setup-tests-pipeline.ps1 - displayName: 'Setup test environment -- Install the Core Tools' - - pwsh: | - $currDir = Get-Location - $Env:Path = $Env:Path+";$currDir/Azure.Functions.Cli" - func --version - displayName: 'Setup Core Tools - Windows' - - task: DotNetCoreCLI@2 - retryCountOnTaskFailure: 3 - inputs: - command: 'test' - projects: | - emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E.csproj - env: - JAVA_HOME: $(JavaHome) - AzureWebJobsStorage: "UseDevelopmentStorage=true" - ENABLE_SDK_TYPES: "true" - displayName: 'Build & Run tests (SDK types)' - continueOnError: false \ No newline at end of file + continueOnError: false \ No newline at end of file diff --git a/eng/ci/templates/official/jobs/build-artifacts.yml b/eng/ci/templates/official/jobs/build-artifacts.yml index b4601011..d9c4a6b5 100644 --- a/eng/ci/templates/official/jobs/build-artifacts.yml +++ b/eng/ci/templates/official/jobs/build-artifacts.yml @@ -38,9 +38,6 @@ jobs: - pwsh: | java -version displayName: 'Check default java version' - - pwsh: | - .\installAdditionsLocally.ps1 - displayName: 'Install java-additions locally' - pwsh: | if ("$(isRelease)"){ $buildNumber="$(Build.SourceBranchName)" diff --git a/pom.xml b/pom.xml index 4a809e45..f6217627 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 1.8 1.3.0 1.1.0 - 1.0.0 + 1.0.1 2.2.0 5.9.1 4.11.0 @@ -308,7 +308,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.1 + 3.5.3 ${project.build.directory} @@ -422,5 +422,30 @@ + + jdk-17-plus + + + [17,) + + + 5.18.0 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang.reflect=ALL-UNNAMED + + + + + + \ No newline at end of file diff --git a/src/main/java/com/microsoft/azure/functions/worker/Constants.java b/src/main/java/com/microsoft/azure/functions/worker/Constants.java index 54bf51d7..87b28344 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/Constants.java +++ b/src/main/java/com/microsoft/azure/functions/worker/Constants.java @@ -17,4 +17,5 @@ private Constants(){} public final static String HAS_IMPLICIT_OUTPUT_QUALIFIED_NAME = "com.microsoft.azure.functions.annotation.HasImplicitOutput"; public static final String JAVA_ENABLE_OPENTELEMETRY = "JAVA_ENABLE_OPENTELEMETRY"; public static final String JAVA_APPLICATIONINSIGHTS_ENABLE_TELEMETRY = "JAVA_APPLICATIONINSIGHTS_ENABLE_TELEMETRY"; + public static final String JAVA_ENABLE_SDK_TYPES = "JAVA_ENABLE_SDK_TYPES"; } diff --git a/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java b/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java index b005d787..1ccd91c9 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java +++ b/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java @@ -53,17 +53,15 @@ public void addExecutionContextSource(ExecutionContextDataSource executionContex public Optional getDataByName(String name, Type target) { DataSource parameterDataSource = this.inputSources.get(name); - if (parameterDataSource == null) { - Optional>> firstEntry = this.inputSources.entrySet().stream().findFirst(); - if (firstEntry.isPresent()) { - DataSource subDict = firstEntry.get().getValue(); - return subDict.computeByName(name, target); - } - } if (parameterDataSource == null) { throw new RuntimeException("Cannot find matched parameter name of customer function, please check if customer function is defined correctly"); } - return parameterDataSource.computeByName(name, target); + return parameterDataSource.computeByName(name, target); + } + + public Optional getDataByNameFromInputSource(String name, Type target, String inputSourceName) { + return Optional.ofNullable(this.inputSources.get(inputSourceName)) + .flatMap(ds -> ds.computeByName(name, target)); } public Optional getTriggerMetatDataByName(String name, Type target) { diff --git a/src/main/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBroker.java b/src/main/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBroker.java index 0d0641e6..705febe2 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBroker.java +++ b/src/main/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBroker.java @@ -28,6 +28,8 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import static com.microsoft.azure.functions.worker.Constants.JAVA_ENABLE_SDK_TYPES; + /** * A broker between JAR methods and the function RPC. It can load methods using * reflection, and invoke them at runtime. Thread-Safety: Multiple thread. @@ -47,7 +49,7 @@ public class JavaFunctionBroker { private final SdkParameterAnalyzer sdkParameterAnalyzer = new SdkParameterAnalyzer(); private final WorkerObjectCache workerObjectCache; private static final boolean JAVA_ENABLE_SDK_TYPES_FLAG = - Boolean.parseBoolean(System.getenv("JAVA_ENABLE_SDK_TYPES")); + Boolean.parseBoolean(System.getenv(JAVA_ENABLE_SDK_TYPES)); private ClassLoader userContextClassLoader; private FunctionInstanceInjector newInstanceInjector() { diff --git a/src/main/java/com/microsoft/azure/functions/worker/chain/SdkTypeMiddleware.java b/src/main/java/com/microsoft/azure/functions/worker/chain/SdkTypeMiddleware.java index 08f6c60e..73cf9136 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/chain/SdkTypeMiddleware.java +++ b/src/main/java/com/microsoft/azure/functions/worker/chain/SdkTypeMiddleware.java @@ -4,6 +4,7 @@ import com.microsoft.azure.functions.internal.spi.middleware.Middleware; import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareChain; import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareContext; +import com.microsoft.azure.functions.worker.binding.BindingData; import com.microsoft.azure.functions.worker.binding.BindingDataStore; import com.microsoft.azure.functions.worker.binding.ExecutionContextDataSource; import com.microsoft.azure.functions.worker.broker.ParamBindInfo; @@ -15,7 +16,9 @@ import com.microsoft.azure.functions.sdktype.SdkTypeMetaData; import java.lang.reflect.Parameter; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.logging.Logger; @@ -49,20 +52,13 @@ public void invoke(MiddlewareContext context, MiddlewareChain chain) throws Exce try { ExecutionContextDataSource execCtx = (ExecutionContextDataSource) context; - BindingDataStore dataStore = execCtx.getDataStore(); WorkerObjectCache cache = execCtx.getCache(); for (SdkTypeMetaData metaData : this.sdkTypesMetaData) { - Set requiredKeys = metaData.getRequiredFields(); - - for (String key : requiredKeys) { - Object val = dataStore.getDataByName(key, String.class) - .map(b -> b.getValue()) - .orElseThrow(() -> new IllegalArgumentException("Missing " + key)); - - metaData.setFieldValue(key, val); - } + Parameter param = metaData.getParam(); + ParamBindInfo paramBindInfo = new ParamBindInfo(param); + fillMetaData(execCtx, metaData, paramBindInfo.getName()); SdkType sdkType = this.sdkTypeRegistry.createSdkType(metaData); Object instance = null; @@ -84,8 +80,6 @@ public void invoke(MiddlewareContext context, MiddlewareChain chain) throws Exce } // update in data store - Parameter param = metaData.getParam(); - ParamBindInfo paramBindInfo = new ParamBindInfo(param); execCtx.updateParameterValue(paramBindInfo.getName(), instance); LOGGER.info("SdkTypeMiddleware: Successfully created instance for param " @@ -97,4 +91,43 @@ public void invoke(MiddlewareContext context, MiddlewareChain chain) throws Exce chain.doNext(context); } + + /** + * Populate the {@link SdkTypeMetaData} with required field values and ensure + * every required key is present. Throws a single informative exception if any + * are missing. + * + * After successfully filling in all required data, a call to + * SdkTypeMetaData::parseAndVerify is made using the input metaData. + */ + private void fillMetaData(ExecutionContextDataSource execCtx, SdkTypeMetaData metaData, String inputSourceName) { + + BindingDataStore dataStore = execCtx.getDataStore(); + Set requiredKeys = metaData.getRequiredFields(); + List missing = new ArrayList<>(); + + for (String key : requiredKeys) { + Optional opt = dataStore + .getDataByNameFromInputSource(key, String.class, inputSourceName); + + if (opt.isPresent()) { + metaData.setFieldValue(key, opt.get().getValue()); + } else { + missing.add(key); + } + } + + if (missing.isEmpty()) { + metaData.parseAndVerify(); + } else { + throw new IllegalArgumentException(String.format( + "Parameter '%s' in function '%s' (invocation %s) is missing %d required " + + "key(s): %s", + inputSourceName, + execCtx.getFunctionName(), + execCtx.getInvocationId(), + missing.size(), + String.join(", ", missing))); + } + } } \ No newline at end of file