├── .github ├── policies │ └── resourceManagement.yml └── pull_request_template.md ├── .gitignore ├── CODEOWNERS ├── CONTRIBUTING.md ├── CodeQL.yml ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── SECURITY.md ├── annotationLib ├── azure-functions-java-library-2.0.1-SNAPSHOT.jar └── warmup-httptrigger.jar ├── azure-function-examples └── local.settings.json ├── emulatedtests ├── Azure.Functions.Java.Tests.E2E │ ├── Azure.Functions.Java.Tests.E2E.sln │ └── Azure.Functions.Java.Tests.E2E │ │ ├── Azure.Functions.Java.Tests.E2E.csproj │ │ ├── Constants.cs │ │ ├── Fixtures │ │ ├── FixtureHelpers.cs │ │ └── FunctionAppFixture.cs │ │ ├── Helpers │ │ └── StorageHelpers.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── StorageEndToEndTests.cs │ │ └── Utilities.cs ├── README.md ├── confluent_cloud_cacert.pem ├── host.json ├── pom.xml ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── microsoft │ │ │ └── azure │ │ │ └── functions │ │ │ └── endtoend │ │ │ ├── BindingNameTests.java │ │ │ ├── BlobTriggerSdkTypesTests.java │ │ │ ├── BlobTriggerTests.java │ │ │ ├── HttpTriggerTests.java │ │ │ ├── QueueTriggerTests.java │ │ │ ├── TableTests.java │ │ │ └── TimerTriggerTests.java │ │ └── resources │ │ └── logback.xml └── utils │ ├── Config.json │ └── docker-compose.yml ├── endtoendtests ├── Azure.Functions.Java.Tests.E2E │ ├── Azure.Functions.Java.Tests.E2E.sln │ └── Azure.Functions.Java.Tests.E2E │ │ ├── Azure.Functions.Java.Tests.E2E.csproj │ │ ├── Constants.cs │ │ ├── CosmosDBEndToEndTests.cs │ │ ├── DurableEndToEndTests.cs │ │ ├── EventGridEndToEndTests.cs │ │ ├── EventHubsEndToEndTests.cs │ │ ├── Fixtures │ │ ├── FixtureHelpers.cs │ │ └── FunctionAppFixture.cs │ │ ├── Helpers │ │ ├── AppInsight │ │ │ ├── AppInsightHelper.cs │ │ │ ├── Enums.cs │ │ │ └── RestApiJsonSerialize.cs │ │ ├── CosmosDBHelpers.cs │ │ ├── EventGridHelpers.cs │ │ ├── EventHubQueueHelpers.cs │ │ ├── ServiceBusHelpers.cs │ │ └── StorageHelpers.cs │ │ ├── HttpEndToEndTests.cs │ │ ├── KafkaEndToEndTests.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── ServiceBusEndToEndTests.cs │ │ ├── SqlEndToEndTests.cs │ │ ├── StorageEndToEndTests.cs │ │ └── Utilities.cs ├── README.md ├── confluent_cloud_cacert.pem ├── host.json ├── local.settings.json ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── microsoft │ │ └── azure │ │ └── functions │ │ └── endtoend │ │ ├── BindingNameTests.java │ │ ├── BlobTriggerTests.java │ │ ├── CosmosDBTriggerTests.java │ │ ├── DurableFunctions.java │ │ ├── EventGridTriggerTests.java │ │ ├── EventHubTriggerTests.java │ │ ├── HttpTriggerTests.java │ │ ├── KafkaTriggerTests.java │ │ ├── QueueTriggerTests.java │ │ ├── ServiceBusQueueTriggerTests.java │ │ ├── ServiceBusTopicTriggerTests.java │ │ ├── SqlTriggerTests.java │ │ ├── TableTests.java │ │ ├── TimerTriggerTests.java │ │ └── springcloud │ │ ├── Config.java │ │ └── SpringCloudHandler.java │ └── resources │ └── logback.xml ├── eng └── ci │ ├── code-mirror.yml │ ├── integration-tests.yml │ ├── official-build.yml │ ├── public-build.yml │ └── templates │ ├── jobs │ ├── build.yml │ ├── run-emulated-tests-linux.yml │ └── run-emulated-tests-windows.yml │ └── official │ └── jobs │ ├── build-artifacts.yml │ ├── run-e2e-tests-linux.yml │ └── run-e2e-tests-windows.yml ├── installAdditionsLocally.ps1 ├── installMavenPluginLocally.ps1 ├── mvnBuild.bat ├── mvnBuildAdditions.bat ├── mvnBuildSkipTests.bat ├── package-pipeline.ps1 ├── pom.xml ├── samples ├── dependency-injection-example │ ├── README.md │ ├── dagger-function │ │ ├── README.md │ │ ├── host.json │ │ ├── img.png │ │ ├── img_1.png │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── azfs │ │ │ │ ├── Function.java │ │ │ │ ├── component │ │ │ │ └── FunctionComponent.java │ │ │ │ ├── dihook │ │ │ │ └── MyFunctionInstanceInjector.java │ │ │ │ ├── model │ │ │ │ └── Communicator.java │ │ │ │ └── module │ │ │ │ └── FunctionModule.java │ │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector │ └── guice-function │ │ ├── .gitignore │ │ ├── README.md │ │ ├── host.json │ │ ├── img.png │ │ ├── img_2.png │ │ ├── pom.xml │ │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── azfs │ │ │ ├── BasicModule.java │ │ │ ├── Communicator.java │ │ │ ├── DefaultCommunicatorImpl.java │ │ │ ├── Function.java │ │ │ └── dihook │ │ │ └── FunctionGuiceFactory.java │ │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector └── spring-cloud-example │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── example │ │ │ ├── Application.java │ │ │ ├── hello │ │ │ ├── Hello.java │ │ │ ├── HelloHandler.java │ │ │ └── model │ │ │ │ ├── Greeting.java │ │ │ │ └── User.java │ │ │ └── uppercase │ │ │ ├── Config.java │ │ │ ├── EchoHandler.java │ │ │ └── UppercaseHandler.java │ └── resources │ │ └── host.json │ └── test │ └── java │ └── example │ └── hello │ └── HelloTest.java ├── setup-tests-pipeline.ps1 ├── src ├── main │ ├── azure-functions-language-worker-protobuf │ │ ├── .gitignore │ │ ├── CODEOWNERS │ │ ├── LICENSE │ │ ├── README.md │ │ └── src │ │ │ └── proto │ │ │ ├── FunctionRpc.proto │ │ │ ├── identity │ │ │ └── ClaimsIdentityRpc.proto │ │ │ └── shared │ │ │ └── NullableTypes.proto │ └── java │ │ └── com │ │ └── microsoft │ │ └── azure │ │ └── functions │ │ └── worker │ │ ├── Application.java │ │ ├── Constants.java │ │ ├── IApplication.java │ │ ├── JavaWorkerClient.java │ │ ├── Util.java │ │ ├── WorkerLogManager.java │ │ ├── binding │ │ ├── BindingData.java │ │ ├── BindingDataStore.java │ │ ├── BindingDefinition.java │ │ ├── DataOperations.java │ │ ├── DataSource.java │ │ ├── DataTarget.java │ │ ├── ExecutionContextDataSource.java │ │ ├── ExecutionRetryContext.java │ │ ├── ExecutionTraceContext.java │ │ ├── RpcByteArrayDataSource.java │ │ ├── RpcCollectionByteArrayDataSource.java │ │ ├── RpcCollectionDoubleDataSource.java │ │ ├── RpcCollectionLongDataSource.java │ │ ├── RpcCollectionStringDataSource.java │ │ ├── RpcEmptyDataSource.java │ │ ├── RpcHttpDataTarget.java │ │ ├── RpcHttpRequestDataSource.java │ │ ├── RpcIntegerDataSource.java │ │ ├── RpcJsonDataSource.java │ │ ├── RpcModelBindingDataSource.java │ │ ├── RpcRealNumberDataSource.java │ │ ├── RpcStringDataSource.java │ │ └── RpcUnspecifiedDataTarget.java │ │ ├── broker │ │ ├── CoreTypeResolver.java │ │ ├── EnhancedJavaMethodExecutorImpl.java │ │ ├── FunctionDefinition.java │ │ ├── JavaFunctionBroker.java │ │ ├── JavaMethodExecutor.java │ │ ├── JavaMethodExecutorImpl.java │ │ ├── JavaMethodExecutors.java │ │ ├── JavaMethodInvokeInfo.java │ │ ├── MethodBindInfo.java │ │ ├── ParamBindInfo.java │ │ └── ParameterResolver.java │ │ ├── cache │ │ ├── WorkerObjectCache.java │ │ └── WorkerObjectCacheKey.java │ │ ├── chain │ │ ├── FunctionExecutionMiddleware.java │ │ ├── InvocationChain.java │ │ ├── InvocationChainFactory.java │ │ └── SdkTypeMiddleware.java │ │ ├── description │ │ └── FunctionMethodDescriptor.java │ │ ├── handler │ │ ├── FunctionEnvironmentReloadRequestHandler.java │ │ ├── FunctionLoadRequestHandler.java │ │ ├── InvocationRequestHandler.java │ │ ├── MessageHandler.java │ │ ├── RpcLogHandler.java │ │ ├── StartStreamHandler.java │ │ ├── WorkerInitRequestHandler.java │ │ ├── WorkerStatusRequestHandler.java │ │ ├── WorkerTerminateRequestHandler.java │ │ └── WorkerWarmupHandler.java │ │ └── reflect │ │ ├── ClassLoaderProvider.java │ │ ├── DefaultClassLoaderProvider.java │ │ ├── EnhancedClassLoaderProvider.java │ │ └── FactoryClassLoader.java └── test │ ├── java │ └── com │ │ └── microsoft │ │ └── azure │ │ └── functions │ │ └── worker │ │ ├── UtilTest.java │ │ ├── binding │ │ └── tests │ │ │ ├── RpcByteArrayDataSourceTest.java │ │ │ ├── RpcCollectionByteArrayDataSourceTest.java │ │ │ ├── RpcCollectionDoubleDataSourceTest.java │ │ │ ├── RpcCollectionLongDataSourceTest.java │ │ │ ├── RpcCollectionStringDataSourceTest.java │ │ │ ├── RpcHttpRequestDataSourceTest.java │ │ │ ├── RpcIntegerDataSourceTest.java │ │ │ └── RpcStringDataSourceTest.java │ │ ├── broker │ │ ├── JavaFunctionBrokerTest.java │ │ ├── ParameterResolverTest.java │ │ └── tests │ │ │ ├── CoreTypeResolverTest.java │ │ │ ├── FunctionDefinitionTest.java │ │ │ ├── FunctionDefinitionTests.java │ │ │ ├── InvalidCustomBinding.java │ │ │ ├── TestCustomBinding.java │ │ │ ├── TestCustomBindingNoName.java │ │ │ ├── TestFunctionsClass.jar │ │ │ └── TestFunctionsClass.java │ │ ├── functional │ │ └── tests │ │ │ └── SimpleParamReturnTest.java │ │ ├── handler │ │ └── tests │ │ │ └── FunctionEnvironmentReloadRequestHandlerTest.java │ │ └── test │ │ ├── ExecutionTraceContextTest.java │ │ └── utilities │ │ ├── FunctionsTestBase.java │ │ └── FunctionsTestHost.java │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── tools ├── .images │ ├── remote-config.png │ └── worker-debug-configuration.png ├── AzureFunctionsJavaWorker.nuspec ├── README.md └── scripts │ ├── GetBlobStorage.ps1 │ └── UpdateBlobStorage.ps1 ├── warmup-function ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── microsoft │ └── azure │ └── functions │ └── warmup │ └── java │ ├── Function.java │ └── HttpTrigger.java └── worker.config.json /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Issue describing the changes in this PR 4 | 5 | resolves #issue_for_this_pr 6 | 7 | ### Pull request checklist 8 | 9 | * [ ] My changes **do not** require documentation changes 10 | * [ ] Otherwise: Documentation issue linked to PR 11 | * [ ] My changes **should not** be added to the release notes for the next release 12 | * [ ] Otherwise: I've added my notes to `release_notes.md` 13 | * [ ] My changes **do not** need to be backported to a previous version 14 | * [ ] Otherwise: Backport tracked by issue/PR #issue_or_pr 15 | * [ ] I have added all required tests (Unit tests, E2E tests) 16 | 17 | 18 | ### Additional information 19 | 20 | Additional PR information -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/about-codeowners/ 2 | # for more info about CODEOWNERS file 3 | # 4 | # It uses the same pattern rule for gitignore file 5 | # https://git-scm.com/docs/gitignore#_pattern_format 6 | # 7 | 8 | # 9 | # AZURE FUNCTIONS TEAM 10 | # For all file changes, github would automatically include the following people in the PRs. 11 | # 12 | * @vrdmr @gavin-aguiar @hallvictoria @ahmedmuhsin @swapnil-nagar 13 | -------------------------------------------------------------------------------- /CodeQL.yml: -------------------------------------------------------------------------------- 1 | path_classifiers: 2 | generated: 3 | # Classify generated code. 4 | - extract/inst 5 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | #### Investigative information 7 | 8 | Please provide the following: 9 | 10 | - Timestamp: 11 | - Function App name: 12 | - Function name(s) (as appropriate): 13 | - Invocation ID: 14 | - Region: 15 | 16 | 19 | 20 | #### Repro steps 21 | 22 | Provide the steps required to reproduce the problem: 23 | 24 | 30 | 31 | #### Expected behavior 32 | 33 | Provide a description of the expected behavior. 34 | 35 | 40 | 41 | #### Actual behavior 42 | 43 | Provide a description of the actual behavior observed. 44 | 45 | 50 | 51 | #### Known workarounds 52 | 53 | Provide a description of any known workarounds. 54 | 55 | 60 | 61 | #### Related information 62 | 63 | Provide any related information 64 | 65 | * Programming language used 66 | * Links to source 67 | * Bindings used 68 | 95 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /annotationLib/azure-functions-java-library-2.0.1-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/annotationLib/azure-functions-java-library-2.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /annotationLib/warmup-httptrigger.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/annotationLib/warmup-httptrigger.jar -------------------------------------------------------------------------------- /azure-function-examples/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "", 5 | "FUNCTIONS_WORKER_RUNTIME": "java" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2042 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Functions.Java.Tests.E2E", "Azure.Functions.Java.Tests.E2E\Azure.Functions.Java.Tests.E2E.csproj", "{830D2261-3E35-46A6-8B52-08233D852ED0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {830D2261-3E35-46A6-8B52-08233D852ED0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {830D2261-3E35-46A6-8B52-08233D852ED0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {830D2261-3E35-46A6-8B52-08233D852ED0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {830D2261-3E35-46A6-8B52-08233D852ED0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {45378AFC-889B-4751-A494-44B73F8EA77E} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Always 21 | 22 | 23 | Always 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Azure.Functions.Java.Tests.E2E 7 | { 8 | public static class Constants 9 | { 10 | public static string FunctionsHostUrl = Environment.GetEnvironmentVariable("FunctionAppUrl") ?? "http://localhost:7071"; 11 | public static string StorageConnectionStringSetting = Environment.GetEnvironmentVariable("AzureWebJobsStorage"); 12 | 13 | 14 | //Queue tests 15 | public static string OutputBindingQueueName = "test-output-java"; 16 | public static string InputBindingQueueName = "test-input-java"; 17 | public static string OutputBindingQueueNamePOJO = "test-output-java-pojo"; 18 | public static string InputBindingQueueNamePOJO = "test-input-java-pojo"; 19 | public static string InputBindingQueueNameMetadata = "test-input-java-metadata"; 20 | public static string OutputBindingQueueNameMetadata = "test-output-java-metadata"; 21 | public static string TestQueueMessage = "Hello, World"; 22 | 23 | //Blob tests 24 | public static string TriggerInputBindingBlobContainer = "test-triggerinput-java-new"; 25 | public static string InputBindingBlobContainer = "test-input-java-new"; 26 | public static string OutputBindingBlobContainer = "test-output-java-new"; 27 | public static string TriggerInputBindingBlobClientSdk = "test-triggerinput-blobclient"; 28 | public static string TriggerInputBindingBlobContainerClientSdk = "test-triggerinput-blobcontclient"; 29 | 30 | // Xunit Fixtures and Collections 31 | public const string FunctionAppCollectionName = "FunctionAppCollection"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Fixtures/FixtureHelpers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace Azure.Functions.Java.Tests.E2E 10 | { 11 | public static class FixtureHelpers 12 | { 13 | public static Process GetFuncHostProcess(bool enableAuth = false) 14 | { 15 | var funcHostProcess = new Process(); 16 | var rootDir = Path.GetFullPath(@"../../../../../.."); 17 | 18 | funcHostProcess.StartInfo.UseShellExecute = false; 19 | funcHostProcess.StartInfo.RedirectStandardError = true; 20 | funcHostProcess.StartInfo.RedirectStandardOutput = true; 21 | funcHostProcess.StartInfo.CreateNoWindow = true; 22 | funcHostProcess.StartInfo.WorkingDirectory = Path.Combine(rootDir, @"emulatedtests/target/azure-functions/azure-functions-java-emulatedtests"); 23 | if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 24 | { 25 | funcHostProcess.StartInfo.FileName = Path.Combine(rootDir, @"Azure.Functions.Cli/func.exe"); 26 | } 27 | else 28 | { 29 | funcHostProcess.StartInfo.FileName = Path.Combine(rootDir, @"Azure.Functions.Cli/func"); 30 | } 31 | funcHostProcess.StartInfo.ArgumentList.Add("start"); 32 | if (enableAuth) 33 | { 34 | funcHostProcess.StartInfo.ArgumentList.Add("--enableAuth"); 35 | } 36 | 37 | return funcHostProcess; 38 | } 39 | 40 | public static void StartProcessWithLogging(Process funcProcess) 41 | { 42 | funcProcess.ErrorDataReceived += (sender, e) => Console.WriteLine(e?.Data); 43 | funcProcess.OutputDataReceived += (sender, e) => Console.WriteLine(e?.Data); 44 | 45 | funcProcess.Start(); 46 | 47 | funcProcess.BeginErrorReadLine(); 48 | funcProcess.BeginOutputReadLine(); 49 | } 50 | 51 | public static void KillExistingFuncHosts() 52 | { 53 | foreach (var func in Process.GetProcessesByName("func")) 54 | { 55 | func.Kill(); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Fixtures/FunctionAppFixture.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using Microsoft.Extensions.Logging; 8 | using Xunit; 9 | 10 | namespace Azure.Functions.Java.Tests.E2E 11 | { 12 | public class FunctionAppFixture : IDisposable 13 | { 14 | private readonly ILogger _logger; 15 | private bool _disposed; 16 | private Process _funcProcess; 17 | 18 | public FunctionAppFixture() 19 | { 20 | // initialize logging 21 | #pragma warning disable CS0618 // Type or member is obsolete 22 | ILoggerFactory loggerFactory = new LoggerFactory().AddConsole(); 23 | #pragma warning restore CS0618 // Type or member is obsolete 24 | _logger = loggerFactory.CreateLogger(); 25 | 26 | // start host via CLI if testing locally 27 | if (Constants.FunctionsHostUrl.Contains("localhost")) 28 | { 29 | // kill existing func processes 30 | _logger.LogInformation("Shutting down any running functions hosts.."); 31 | FixtureHelpers.KillExistingFuncHosts(); 32 | 33 | // start functions process 34 | _logger.LogInformation($"Starting functions host for {Constants.FunctionAppCollectionName}.."); 35 | _funcProcess = FixtureHelpers.GetFuncHostProcess(); 36 | 37 | FixtureHelpers.StartProcessWithLogging(_funcProcess); 38 | 39 | _logger.LogInformation($"Waiting for functions host to be ready..."); 40 | Thread.Sleep(TimeSpan.FromSeconds(30)); 41 | } 42 | } 43 | 44 | protected virtual void Dispose(bool disposing) 45 | { 46 | if (!_disposed) 47 | { 48 | if (disposing) 49 | { 50 | _logger.LogInformation("FunctionAppFixture disposing."); 51 | 52 | if (_funcProcess != null) 53 | { 54 | _logger.LogInformation($"Shutting down functions host for {Constants.FunctionAppCollectionName}"); 55 | _funcProcess.Kill(); 56 | _funcProcess.Dispose(); 57 | } 58 | } 59 | 60 | _disposed = true; 61 | } 62 | } 63 | 64 | public void Dispose() 65 | { 66 | Dispose(true); 67 | } 68 | } 69 | 70 | [CollectionDefinition(Constants.FunctionAppCollectionName)] 71 | public class FunctionAppCollection : ICollectionFixture 72 | { 73 | // This class has no code, and is never created. Its purpose is simply 74 | // to be the place to apply [CollectionDefinition] and all the 75 | // ICollectionFixture<> interfaces. 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /emulatedtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Azure.Functions.Java.Tests.E2E": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /emulatedtests/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | }, 11 | "extensionBundle": { 12 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 13 | "version": "[4.*, 5.0.0)" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /emulatedtests/src/main/java/com/microsoft/azure/functions/endtoend/BindingNameTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.microsoft.azure.functions.*; 5 | import java.util.Optional; 6 | 7 | /** 8 | * Azure Functions with Timer trigger. 9 | */ 10 | public class BindingNameTests { 11 | /** 12 | * This function will be invoked with a http request and send back the url of the request back. 13 | */ 14 | @FunctionName("BindingName") 15 | public HttpResponseMessage run( 16 | @HttpTrigger(name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.ANONYMOUS, route = "BindingName/{testMessage}") HttpRequestMessage> request, 17 | @BindingName("testMessage") String route, 18 | final ExecutionContext context) { 19 | context.getLogger().info("Java HTTP trigger processed a request."); 20 | return request.createResponseBuilder(HttpStatus.OK).body(route).build(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /emulatedtests/src/main/java/com/microsoft/azure/functions/endtoend/BlobTriggerSdkTypesTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.azure.storage.blob.BlobClient; 4 | import com.azure.storage.blob.BlobContainerClient; 5 | import com.microsoft.azure.functions.ExecutionContext; 6 | import com.microsoft.azure.functions.OutputBinding; 7 | import com.microsoft.azure.functions.annotation.*; 8 | 9 | import java.io.ByteArrayOutputStream; 10 | 11 | /** 12 | * Azure Functions with Azure Storage Blob. 13 | */ 14 | public class BlobTriggerSdkTypesTests { 15 | /** 16 | * 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. 17 | */ 18 | @FunctionName("BlobTriggerUsingBlobClientToBlobTest") 19 | @StorageAccount("AzureWebJobsStorage") 20 | public void BlobTriggerToBlobTest_BlobClient( 21 | @BlobTrigger(name = "triggerBlob", path = "test-triggerinput-blobclient/{name}", dataType = "binary") BlobClient triggerBlobClient, 22 | @BindingName("name") String fileName, 23 | @BlobOutput(name = "outputBlob", path = "test-output-java-new/testfile.txt", dataType = "binary") OutputBinding outputBlob, 24 | final ExecutionContext context 25 | ) { 26 | context.getLogger().info("BlobTriggerUsingBlobClient triggered for blob: " + fileName); 27 | 28 | // Download the blob content 29 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 30 | triggerBlobClient.downloadStream(outputStream); 31 | 32 | // Set the downloaded content as output 33 | outputBlob.setValue(outputStream.toByteArray()); 34 | context.getLogger().info("Uploaded blob " + fileName + " to container test-output-java-new/testfile.txt"); 35 | } 36 | 37 | /** 38 | * 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. 39 | */ 40 | @FunctionName("BlobTriggerUsingBlobContainerClientToBlobTest") 41 | @StorageAccount("AzureWebJobsStorage") 42 | public void BlobTriggerToBlobTest_BlobContainerClient( 43 | @BlobTrigger(name = "triggerBlob", path = "test-triggerinput-blobcontclient/{name}", dataType = "binary") BlobContainerClient triggerBlobContainerClient, 44 | @BindingName("name") String fileName, 45 | @BlobOutput(name = "outputBlob", path = "test-output-java-new/testfile.txt", dataType = "binary") OutputBinding outputBlob, 46 | final ExecutionContext context 47 | ) { 48 | context.getLogger().info("BlobTriggerUsingBlobContainerClient triggered for blob: " + fileName); 49 | 50 | // Download the blob content 51 | BlobClient triggerBlobClient = triggerBlobContainerClient.getBlobClient(fileName); 52 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 53 | triggerBlobClient.downloadStream(outputStream); 54 | 55 | // Set the downloaded content as output 56 | outputBlob.setValue(outputStream.toByteArray()); 57 | context.getLogger().info("Uploaded blob " + fileName + " to container test-output-java-new/testfile.txt"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /emulatedtests/src/main/java/com/microsoft/azure/functions/endtoend/BlobTriggerTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | 5 | import com.microsoft.azure.functions.*; 6 | 7 | /** 8 | * Azure Functions with Azure Storage Blob. 9 | */ 10 | public class BlobTriggerTests { 11 | /** 12 | * 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. 13 | */ 14 | @FunctionName("BlobTriggerToBlobTest") 15 | @StorageAccount("AzureWebJobsStorage") 16 | public void BlobTriggerToBlobTest( 17 | @BlobTrigger(name = "triggerBlob", path = "test-triggerinput-java-new/{name}", dataType = "binary") byte[] triggerBlob, 18 | @BindingName("name") String fileName, 19 | @BlobInput(name = "inputBlob", path = "test-input-java-new/{name}", dataType = "binary") byte[] inputBlob, 20 | @BlobOutput(name = "outputBlob", path = "test-output-java-new/{name}", dataType = "binary") OutputBinding outputBlob, 21 | final ExecutionContext context 22 | ) { 23 | context.getLogger().info("Java Blob trigger function BlobTriggerToBlobTest processed a blob.\n Name: " + fileName + "\n Size: " + triggerBlob.length + " Bytes"); 24 | outputBlob.setValue(inputBlob); 25 | } 26 | 27 | /* 28 | * Verified via Unit tests. Added test here for sample code 29 | */ 30 | @FunctionName("BlobTriggerPOJOTest") 31 | @StorageAccount("AzureWebJobsStorage") 32 | public void BlobTriggerPOJOTest( 33 | @BlobTrigger(name = "triggerBlob", path = "test-triggerinputpojo-java/{name}") TestBlobData triggerBlobText, 34 | @BindingName("name") String fileName, 35 | @BlobOutput(name = "outputBlob", path = "test-outputpojo-java/{name}") OutputBinding outputBlob, 36 | final ExecutionContext context 37 | ) { 38 | context.getLogger().info("Java Blob trigger function BlobTriggerPOJOTest processed a blob.\n Name: " + fileName + "\n Content: " + triggerBlobText.blobText); 39 | outputBlob.setValue(triggerBlobText); 40 | } 41 | 42 | /* 43 | * Verified via Unit tests. Added test here for sample code 44 | */ 45 | @FunctionName("BlobTriggerStringTest") 46 | @StorageAccount("AzureWebJobsStorage") 47 | public void BlobTriggerStringTest( 48 | @BlobTrigger(name = "triggerBlob", path = "test-triggerinputstring-java/{name}") String triggerBlobText, 49 | @BindingName("name") String fileName, 50 | @BlobOutput(name = "outputBlob", path = "test-outputstring-java/{name}") OutputBinding outputBlob, 51 | final ExecutionContext context 52 | ) { 53 | context.getLogger().info("Java Blob trigger function BlobTriggerStringTest processed a blob.\n Name: " + fileName + "\n Content: " + triggerBlobText); 54 | outputBlob.setValue(triggerBlobText); 55 | } 56 | 57 | public static class TestBlobData { 58 | public String blobText; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /emulatedtests/src/main/java/com/microsoft/azure/functions/endtoend/TableTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.microsoft.azure.functions.*; 5 | import java.util.*; 6 | 7 | /** 8 | * Azure Functions with Azure Storage table. 9 | */ 10 | public class TableTests { 11 | /** 12 | * This function will be invoked when a new queue message is received. 13 | */ 14 | @FunctionName("TableInput") 15 | public void tableInputJava( 16 | @QueueTrigger(name = "message", queueName = "mytablequeue", connection = "AzureWebJobsStorage") String message, 17 | @TableInput(name = "personEntity", tableName = "Person", rowKey = "{queueTrigger}", partitionKey = "firstPartition", connection = "AzureWebJobsStorage") String personEntity, 18 | final ExecutionContext context 19 | ) { 20 | context.getLogger().info("Java Queue trigger function processed a new message: " + message); 21 | context.getLogger().info("Java Table Input function processed a Person entity:" + personEntity); 22 | } 23 | 24 | /** 25 | * This function will be invoked when a new http request is received at the specified path. 26 | */ 27 | @FunctionName("TableOutput") 28 | public void tableOutputJava( 29 | @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 30 | @TableOutput(name = "myOutputTable", tableName = "Person", connection = "AzureWebJobsStorage") OutputBinding myOutputTable, 31 | final ExecutionContext context 32 | ) { 33 | String httpbody = request.getBody().orElse("default"); 34 | myOutputTable.setValue(new Person(httpbody + "Partition", httpbody + "Row", httpbody + "Name")); 35 | context.getLogger().info("Java Table Output function write a new entity into table Person with name: " + httpbody + "Name"); 36 | } 37 | 38 | public static class Person { 39 | public String PartitionKey; 40 | public String RowKey; 41 | public String Name; 42 | 43 | public Person(String p, String r, String n) { 44 | this.PartitionKey = p; 45 | this.RowKey = r; 46 | this.Name = n; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /emulatedtests/src/main/java/com/microsoft/azure/functions/endtoend/TimerTriggerTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.microsoft.azure.functions.*; 5 | import java.time.*; 6 | 7 | /** 8 | * Azure Functions with Timer trigger. 9 | */ 10 | public class TimerTriggerTests { 11 | /** 12 | * This function will be invoked periodically according to the specified schedule. 13 | */ 14 | @FunctionName("TimerTrigger") 15 | public void timerHandler( 16 | @TimerTrigger(name = "timerInfo", schedule = "0 */5 * * * *") String timerInfo, 17 | final ExecutionContext context 18 | ) { 19 | context.getLogger().info("Java Timer trigger function executed at: " + LocalDateTime.now()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /emulatedtests/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /emulatedtests/utils/Config.json: -------------------------------------------------------------------------------- 1 | { 2 | "UserConfig": { 3 | "NamespaceConfig": [ 4 | { 5 | "Type": "EventHub", 6 | "Name": "emulatorNs1", 7 | "Entities": [ 8 | { 9 | "Name": "python-worker-ci-eventhub-batch", 10 | "PartitionCount": 2, 11 | "ConsumerGroups": [ 12 | { 13 | "Name": "cg1" 14 | } 15 | ] 16 | }, 17 | { 18 | "Name": "python-worker-ci-eventhub-batch-metadata", 19 | "PartitionCount": 2, 20 | "ConsumerGroups": [ 21 | { 22 | "Name": "cg1" 23 | } 24 | ] 25 | }, 26 | { 27 | "Name": "python-worker-ci-eventhub-one", 28 | "PartitionCount": 2, 29 | "ConsumerGroups": [ 30 | { 31 | "Name": "cg1" 32 | } 33 | ] 34 | }, 35 | { 36 | "Name": "python-worker-ci-eventhub-one-metadata", 37 | "PartitionCount": 2, 38 | "ConsumerGroups": [ 39 | { 40 | "Name": "cg1" 41 | } 42 | ] 43 | } 44 | ] 45 | } 46 | ], 47 | "LoggingConfig": { 48 | "Type": "File" 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /emulatedtests/utils/docker-compose.yml: -------------------------------------------------------------------------------- 1 | name: microsoft-azure-eventhubs 2 | services: 3 | # Service for the Event Hubs Emulator 4 | emulator: 5 | container_name: "eventhubs-emulator" 6 | image: "mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest" 7 | volumes: 8 | - "./Config.json:/Eventhubs_Emulator/ConfigFiles/Config.json" 9 | ports: 10 | - "5672:5672" 11 | environment: 12 | BLOB_SERVER: azurite 13 | METADATA_SERVER: azurite 14 | ACCEPT_EULA: Y 15 | depends_on: 16 | - azurite 17 | networks: 18 | eh-emulator: 19 | aliases: 20 | - "eventhubs-emulator" 21 | # Service for the Azurite Storage Emulator 22 | azurite: 23 | container_name: "azurite" 24 | image: "mcr.microsoft.com/azure-storage/azurite:latest" 25 | ports: 26 | - "10000:10000" 27 | - "10001:10001" 28 | - "10002:10002" 29 | networks: 30 | eh-emulator: 31 | aliases: 32 | - "azurite" 33 | networks: 34 | eh-emulator: -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2042 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Functions.Java.Tests.E2E", "Azure.Functions.Java.Tests.E2E\Azure.Functions.Java.Tests.E2E.csproj", "{830D2261-3E35-46A6-8B52-08233D852ED0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {830D2261-3E35-46A6-8B52-08233D852ED0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {830D2261-3E35-46A6-8B52-08233D852ED0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {830D2261-3E35-46A6-8B52-08233D852ED0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {830D2261-3E35-46A6-8B52-08233D852ED0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {45378AFC-889B-4751-A494-44B73F8EA77E} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Always 26 | 27 | 28 | Always 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/DurableEndToEndTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using Newtonsoft.Json.Linq; 5 | using System; 6 | using System.Net; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | using System.Threading; 10 | 11 | namespace Azure.Functions.Java.Tests.E2E 12 | { 13 | [Collection(Constants.FunctionAppCollectionName)] 14 | public class DurableEndToEndTests 15 | { 16 | private readonly FunctionAppFixture _fixture; 17 | 18 | public DurableEndToEndTests(FunctionAppFixture fixture) 19 | { 20 | _fixture = fixture; 21 | } 22 | 23 | [Fact] 24 | public async Task Durable_OrchestrationCompletes() 25 | { 26 | JObject result = await Utilities.StartOrchestration("StartOrchestration", HttpStatusCode.Created); 27 | Assert.NotNull(result); 28 | 29 | String statusUrl = result["statusQueryGetUri"].ToString(); 30 | 31 | int retryCount = 15; 32 | bool success = false; 33 | while(retryCount > 0) 34 | { 35 | 36 | result = await Utilities.InvokeUri(statusUrl); 37 | string runtimeStatus = result["runtimeStatus"].ToString(); 38 | Console.WriteLine($"Orchestration is {runtimeStatus}"); 39 | 40 | if (runtimeStatus.Equals("Completed")) { 41 | success = true; 42 | break; 43 | } 44 | 45 | Thread.Sleep(TimeSpan.FromSeconds(1)); 46 | retryCount -= 1; 47 | } 48 | 49 | Assert.True(success); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Fixtures/FixtureHelpers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace Azure.Functions.Java.Tests.E2E 10 | { 11 | public static class FixtureHelpers 12 | { 13 | public static Process GetFuncHostProcess(bool enableAuth = false) 14 | { 15 | var funcHostProcess = new Process(); 16 | var rootDir = Path.GetFullPath(@"../../../../../.."); 17 | 18 | funcHostProcess.StartInfo.UseShellExecute = false; 19 | funcHostProcess.StartInfo.RedirectStandardError = true; 20 | funcHostProcess.StartInfo.RedirectStandardOutput = true; 21 | funcHostProcess.StartInfo.CreateNoWindow = true; 22 | funcHostProcess.StartInfo.WorkingDirectory = Path.Combine(rootDir, @"endtoendtests/target/azure-functions/azure-functions-java-endtoendtests"); 23 | if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 24 | { 25 | funcHostProcess.StartInfo.FileName = Path.Combine(rootDir, @"Azure.Functions.Cli/func.exe"); 26 | } 27 | else 28 | { 29 | funcHostProcess.StartInfo.FileName = Path.Combine(rootDir, @"Azure.Functions.Cli/func"); 30 | } 31 | funcHostProcess.StartInfo.ArgumentList.Add("start"); 32 | if (enableAuth) 33 | { 34 | funcHostProcess.StartInfo.ArgumentList.Add("--enableAuth"); 35 | } 36 | 37 | return funcHostProcess; 38 | } 39 | 40 | public static void StartProcessWithLogging(Process funcProcess) 41 | { 42 | funcProcess.ErrorDataReceived += (sender, e) => Console.WriteLine(e?.Data); 43 | funcProcess.OutputDataReceived += (sender, e) => Console.WriteLine(e?.Data); 44 | 45 | funcProcess.Start(); 46 | 47 | funcProcess.BeginErrorReadLine(); 48 | funcProcess.BeginOutputReadLine(); 49 | } 50 | 51 | public static void KillExistingFuncHosts() 52 | { 53 | foreach (var func in Process.GetProcessesByName("func")) 54 | { 55 | func.Kill(); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Fixtures/FunctionAppFixture.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using Microsoft.Extensions.Logging; 8 | using Xunit; 9 | 10 | namespace Azure.Functions.Java.Tests.E2E 11 | { 12 | public class FunctionAppFixture : IDisposable 13 | { 14 | private readonly ILogger _logger; 15 | private bool _disposed; 16 | private Process _funcProcess; 17 | 18 | public FunctionAppFixture() 19 | { 20 | // initialize logging 21 | #pragma warning disable CS0618 // Type or member is obsolete 22 | ILoggerFactory loggerFactory = new LoggerFactory().AddConsole(); 23 | #pragma warning restore CS0618 // Type or member is obsolete 24 | _logger = loggerFactory.CreateLogger(); 25 | 26 | // start host via CLI if testing locally 27 | if (Constants.FunctionsHostUrl.Contains("localhost")) 28 | { 29 | // kill existing func processes 30 | _logger.LogInformation("Shutting down any running functions hosts.."); 31 | FixtureHelpers.KillExistingFuncHosts(); 32 | 33 | // start functions process 34 | _logger.LogInformation($"Starting functions host for {Constants.FunctionAppCollectionName}.."); 35 | _funcProcess = FixtureHelpers.GetFuncHostProcess(); 36 | 37 | FixtureHelpers.StartProcessWithLogging(_funcProcess); 38 | 39 | _logger.LogInformation($"Waiting for functions host to be ready..."); 40 | Thread.Sleep(TimeSpan.FromSeconds(30)); 41 | } 42 | } 43 | 44 | protected virtual void Dispose(bool disposing) 45 | { 46 | if (!_disposed) 47 | { 48 | if (disposing) 49 | { 50 | _logger.LogInformation("FunctionAppFixture disposing."); 51 | 52 | if (_funcProcess != null) 53 | { 54 | _logger.LogInformation($"Shutting down functions host for {Constants.FunctionAppCollectionName}"); 55 | _funcProcess.Kill(); 56 | _funcProcess.Dispose(); 57 | } 58 | } 59 | 60 | _disposed = true; 61 | } 62 | } 63 | 64 | public void Dispose() 65 | { 66 | Dispose(true); 67 | } 68 | } 69 | 70 | [CollectionDefinition(Constants.FunctionAppCollectionName)] 71 | public class FunctionAppCollection : ICollectionFixture 72 | { 73 | // This class has no code, and is never created. Its purpose is simply 74 | // to be the place to apply [CollectionDefinition] and all the 75 | // ICollectionFixture<> interfaces. 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/AppInsight/Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace E2ETestCases.Utils 6 | { 7 | // Query result types 8 | public enum QueryType 9 | { requests, dependencies, exceptions, traces, statsbeat } 10 | } 11 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/EventGridHelpers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using Newtonsoft.Json.Linq; 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | using Microsoft.Azure.EventGrid; 11 | using Microsoft.Azure.EventGrid.Models; 12 | 13 | namespace Azure.Functions.Java.Tests.E2E.Helpers 14 | { 15 | public class EventGridHelpers 16 | { 17 | private static EventGridClient _eventGridClient; 18 | 19 | private static string topicHostname = null; 20 | 21 | public EventGridHelpers(string eventGridTopicEndpoint, string eventGridTopicKey) 22 | { 23 | TopicCredentials topicCredentials = new TopicCredentials(eventGridTopicKey); 24 | _eventGridClient = new EventGridClient(topicCredentials); 25 | 26 | topicHostname = new Uri(eventGridTopicEndpoint).Host; 27 | } 28 | 29 | public async Task SendEventAsync(string eventId, string subject, 30 | JObject data, string eventType, DateTime eventDateTime, string dataVersion) 31 | { 32 | List events = new List(); 33 | var eventGridEvent = new EventGridEvent(eventId, subject, data, 34 | eventType, eventDateTime, dataVersion); 35 | events.Add(eventGridEvent); 36 | 37 | await _eventGridClient.PublishEventsAsync(topicHostname, events); 38 | } 39 | 40 | public static async Task SendEventsAsync(List eventGridEvents) 41 | { 42 | await _eventGridClient.PublishEventsAsync(topicHostname, eventGridEvents); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/EventHubQueueHelpers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using Microsoft.Azure.EventHubs; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Azure.Functions.Java.Tests.E2E 12 | { 13 | public class EventHubQueueHelpers 14 | { 15 | public static async Task SendJSONMessagesAsync(string eventId, string connectionString) 16 | { 17 | // write 3 events 18 | List events = new List(); 19 | string[] ids = new string[3]; 20 | for (int i = 0; i < 3; i++) 21 | { 22 | ids[i] = eventId + $"TestEvent{i}"; 23 | JObject jo = new JObject 24 | { 25 | { "value", ids[i] } 26 | }; 27 | var evt = new EventData(Encoding.UTF8.GetBytes(jo.ToString(Formatting.None))); 28 | evt.Properties.Add("TestIndex", i); 29 | events.Add(evt); 30 | } 31 | 32 | EventHubsConnectionStringBuilder builder = new EventHubsConnectionStringBuilder(connectionString); 33 | builder.EntityPath = Constants.InputJsonEventHubName; 34 | EventHubClient eventHubClient = EventHubClient.CreateFromConnectionString(builder.ToString()); 35 | await eventHubClient.SendAsync(events); 36 | } 37 | 38 | public static async Task SendMessagesAsync(string eventId, string evenHubName, string connectionString) 39 | { 40 | // write 3 events 41 | List events = new List(); 42 | string[] ids = new string[3]; 43 | for (int i = 0; i < 3; i++) 44 | { 45 | ids[i] = eventId + $"TestEvent{i}"; 46 | var evt = new EventData(Encoding.UTF8.GetBytes(ids[i])); 47 | evt.Properties.Add("TestIndex", i); 48 | events.Add(evt); 49 | } 50 | 51 | EventHubsConnectionStringBuilder builder = new EventHubsConnectionStringBuilder(connectionString); 52 | builder.EntityPath = evenHubName; 53 | EventHubClient eventHubClient = EventHubClient.CreateFromConnectionString(builder.ToString()); 54 | await eventHubClient.SendAsync(events); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/ServiceBusHelpers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using Microsoft.Azure.ServiceBus; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Azure.Functions.Java.Tests.E2E 12 | { 13 | public class ServiceBusHelpers 14 | { 15 | public static async Task SendMessagesAsync(string eventId, string serviceBusName, string connectionString) 16 | { 17 | byte[] messageBody = System.Text.Encoding.ASCII.GetBytes(eventId); 18 | ServiceBusConnectionStringBuilder builder = new ServiceBusConnectionStringBuilder(connectionString); 19 | builder.EntityPath = serviceBusName; 20 | QueueClient client = new QueueClient(builder, ReceiveMode.PeekLock); 21 | await client.SendAsync(new Message(messageBody)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/KafkaEndToEndTests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection.Metadata; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | 9 | namespace Azure.Functions.Java.Tests.E2E 10 | { 11 | [Collection(Constants.FunctionAppCollectionName)] 12 | public class KafkaEndToEndTests 13 | { 14 | [Fact] 15 | 16 | public async Task KafkaTriggerAndOutputString_Succeeds() 17 | { 18 | string expectedEventId = Guid.NewGuid().ToString(); 19 | try 20 | { 21 | // Send Message to the Kafka Cluster using Kafka Output 22 | await SetupQueue(Constants.OutputStringOneKafkaQueueName); 23 | Assert.True(await Utilities.InvokeHttpTrigger("HttpTriggerAndKafkaOutput", $"?&message={expectedEventId}", System.Net.HttpStatusCode.OK, expectedEventId)); 24 | // Verify 25 | var queueMessage = await StorageHelpers.ReadFromQueue(Constants.OutputStringOneKafkaQueueName); 26 | Assert.Contains(expectedEventId, queueMessage); 27 | } 28 | finally 29 | { 30 | // Clear queue 31 | await StorageHelpers.ClearQueue(Constants.OutputStringOneKafkaQueueName); 32 | } 33 | } 34 | private static async Task SetupQueue(string queueName) 35 | { 36 | //Clear queue 37 | await StorageHelpers.ClearQueue(queueName); 38 | 39 | //Set up and trigger 40 | await StorageHelpers.CreateQueue(queueName); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Azure.Functions.Java.Tests.E2E": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/SqlEndToEndTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the MIT License. See License.txt in the project root for license information. 3 | 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Net; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Xunit; 11 | 12 | namespace Azure.Functions.Java.Tests.E2E 13 | { 14 | [Collection(Constants.FunctionAppCollectionName)] 15 | public class SqlEndToEndTests 16 | { 17 | private readonly FunctionAppFixture _fixture; 18 | 19 | public SqlEndToEndTests(FunctionAppFixture fixture) 20 | { 21 | this._fixture = fixture; 22 | } 23 | 24 | [Fact] 25 | public async Task SqlInput_Output_Succeeds() 26 | { 27 | TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1); 28 | int id = (int) t.TotalSeconds; 29 | var product = new Dictionary() 30 | { 31 | { "ProductId", id }, 32 | { "Name", "test" }, 33 | { "Cost", 100 } 34 | }; 35 | 36 | var productString = JsonConvert.SerializeObject(product); 37 | // Insert row into Products table using SqlOutput 38 | Assert.True(await Utilities.InvokeHttpTriggerPost("AddProduct", productString, HttpStatusCode.OK)); 39 | 40 | // Read row from Products table using SqlInput 41 | Assert.True(await Utilities.InvokeHttpTrigger("GetProducts", "/" + id.ToString(), HttpStatusCode.OK, productString)); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /endtoendtests/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | }, 11 | "extensionBundle": { 12 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 13 | "version": "[4.*, 5.0.0)" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /endtoendtests/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsServiceBus": "", 5 | "AzureWebJobsEventHubReceiver":"", 6 | "AzureWebJobsEventHubSender":"", 7 | "AzureWebJobsEventHubSender_2":"", 8 | "AzureWebJobsEventHubPath":"", 9 | "CosmosDBDatabaseName":"", 10 | "CosmosDBCollectionName":"", 11 | "BrokerList": "", 12 | "ConfluentCloudUsername": "", 13 | "ConfluentCloudPassword": "", 14 | "SBTopicName":"", 15 | "SBTopicSubName":"", 16 | "SBQueueName":"", 17 | "AzureWebJobsCosmosDBConnectionString":"", 18 | "AzureWebJobsEventGridOutputBindingTopicUriString": "", 19 | "AzureWebJobsEventGridOutputBindingTopicKeyString": "", 20 | "AzureWebJobsSqlConnectionString": "", 21 | "FUNCTIONS_WORKER_RUNTIME": "java" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/BindingNameTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.microsoft.azure.functions.*; 5 | import java.util.Optional; 6 | 7 | /** 8 | * Azure Functions with Timer trigger. 9 | */ 10 | public class BindingNameTests { 11 | /** 12 | * This function will be invoked with a http request and send back the url of the request back. 13 | */ 14 | @FunctionName("BindingName") 15 | public HttpResponseMessage run( 16 | @HttpTrigger(name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.ANONYMOUS, route = "BindingName/{testMessage}") HttpRequestMessage> request, 17 | @BindingName("testMessage") String route, 18 | final ExecutionContext context) { 19 | context.getLogger().info("Java HTTP trigger processed a request."); 20 | return request.createResponseBuilder(HttpStatus.OK).body(route).build(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/BlobTriggerTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | import com.microsoft.azure.functions.*; 9 | 10 | /** 11 | * Azure Functions with Azure Storage Blob. 12 | */ 13 | public class BlobTriggerTests { 14 | /** 15 | * 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. 16 | */ 17 | @FunctionName("BlobTriggerToBlobTest") 18 | @StorageAccount("AzureWebJobsStorage") 19 | public void BlobTriggerToBlobTest( 20 | @BlobTrigger(name = "triggerBlob", path = "test-triggerinput-java-new/{name}", dataType = "binary") byte[] triggerBlob, 21 | @BindingName("name") String fileName, 22 | @BlobInput(name = "inputBlob", path = "test-input-java-new/{name}", dataType = "binary") byte[] inputBlob, 23 | @BlobOutput(name = "outputBlob", path = "test-output-java-new/{name}", dataType = "binary") OutputBinding outputBlob, 24 | final ExecutionContext context 25 | ) { 26 | context.getLogger().info("Java Blob trigger function BlobTriggerToBlobTest processed a blob.\n Name: " + fileName + "\n Size: " + triggerBlob.length + " Bytes"); 27 | outputBlob.setValue(inputBlob); 28 | } 29 | 30 | /* 31 | * Verified via Unit tests. Added test here for sample code 32 | */ 33 | @FunctionName("BlobTriggerPOJOTest") 34 | @StorageAccount("AzureWebJobsStorage") 35 | public void BlobTriggerPOJOTest( 36 | @BlobTrigger(name = "triggerBlob", path = "test-triggerinputpojo-java/{name}") TestBlobData triggerBlobText, 37 | @BindingName("name") String fileName, 38 | @BlobOutput(name = "outputBlob", path = "test-outputpojo-java/{name}") OutputBinding outputBlob, 39 | final ExecutionContext context 40 | ) { 41 | context.getLogger().info("Java Blob trigger function BlobTriggerPOJOTest processed a blob.\n Name: " + fileName + "\n Content: " + triggerBlobText.blobText); 42 | outputBlob.setValue(triggerBlobText); 43 | } 44 | 45 | /* 46 | * Verified via Unit tests. Added test here for sample code 47 | */ 48 | @FunctionName("BlobTriggerStringTest") 49 | @StorageAccount("AzureWebJobsStorage") 50 | public void BlobTriggerStringTest( 51 | @BlobTrigger(name = "triggerBlob", path = "test-triggerinputstring-java/{name}") String triggerBlobText, 52 | @BindingName("name") String fileName, 53 | @BlobOutput(name = "outputBlob", path = "test-outputstring-java/{name}") OutputBinding outputBlob, 54 | final ExecutionContext context 55 | ) { 56 | context.getLogger().info("Java Blob trigger function BlobTriggerStringTest processed a blob.\n Name: " + fileName + "\n Content: " + triggerBlobText); 57 | outputBlob.setValue(triggerBlobText); 58 | } 59 | 60 | public static class TestBlobData { 61 | public String blobText; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/DurableFunctions.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.microsoft.azure.functions.*; 5 | import java.util.*; 6 | 7 | import com.microsoft.durabletask.DurableTaskClient; 8 | import com.microsoft.durabletask.OrchestrationRunner; 9 | import com.microsoft.durabletask.azurefunctions.DurableActivityTrigger; 10 | import com.microsoft.durabletask.azurefunctions.DurableClientContext; 11 | import com.microsoft.durabletask.azurefunctions.DurableClientInput; 12 | import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; 13 | 14 | /** 15 | * Azure Durable Functions with HTTP trigger. 16 | */ 17 | public class DurableFunctions { 18 | /** 19 | * This HTTP-triggered function starts the orchestration. 20 | */ 21 | @FunctionName("StartOrchestration") 22 | public HttpResponseMessage startOrchestration( 23 | @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 24 | @DurableClientInput(name = "durableContext") DurableClientContext durableContext, 25 | final ExecutionContext context) { 26 | context.getLogger().info("Java HTTP trigger processed a request."); 27 | 28 | DurableTaskClient client = durableContext.getClient(); 29 | String instanceId = client.scheduleNewOrchestrationInstance("Cities"); 30 | context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); 31 | return durableContext.createCheckStatusResponse(request, instanceId); 32 | } 33 | 34 | /** 35 | * This is the orchestrator function. The OrchestrationRunner.loadAndRun() static 36 | * method is used to take the function input and execute the orchestrator logic. 37 | */ 38 | @FunctionName("Cities") 39 | public String citiesOrchestrator( 40 | @DurableOrchestrationTrigger(name = "orchestratorRequestProtoBytes") String orchestratorRequestProtoBytes) { 41 | return OrchestrationRunner.loadAndRun(orchestratorRequestProtoBytes, ctx -> { 42 | String result = ""; 43 | result += ctx.callActivity("Capitalize", "Tokyo", String.class).await() + ", "; 44 | result += ctx.callActivity("Capitalize", "London", String.class).await() + ", "; 45 | result += ctx.callActivity("Capitalize", "Seattle", String.class).await() + ", "; 46 | result += ctx.callActivity("Capitalize", "Austin", String.class).await(); 47 | return result; 48 | }); 49 | } 50 | 51 | /** 52 | * This is the activity function that gets invoked by the orchestration. 53 | */ 54 | @FunctionName("Capitalize") 55 | public String capitalize( 56 | @DurableActivityTrigger(name = "name") String name, 57 | final ExecutionContext context) { 58 | context.getLogger().info("Capitalizing: " + name); 59 | return name.toUpperCase(); 60 | } 61 | } -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/KafkaTriggerTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | 4 | import java.util.*; 5 | import com.microsoft.azure.functions.annotation.*; 6 | import com.microsoft.azure.functions.*; 7 | 8 | 9 | 10 | import java.util.Optional; 11 | 12 | public class KafkaTriggerTests { 13 | 14 | @FunctionName("HttpTriggerAndKafkaOutput") 15 | public HttpResponseMessage HttpTriggerAndKafkaOutput( 16 | @HttpTrigger(name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 17 | @KafkaOutput( 18 | name = "httpTriggerAndKafkaOutput", 19 | topic = "ci", 20 | brokerList="%BrokerList%", 21 | username = "%ConfluentCloudUsername%", 22 | password = "%ConfluentCloudPassword%", 23 | authenticationMode = BrokerAuthenticationMode.PLAIN, 24 | sslCaLocation = "confluent_cloud_cacert.pem", 25 | protocol = BrokerProtocol.SASLSSL 26 | ) OutputBinding output, 27 | final ExecutionContext context) { 28 | String message = request.getQueryParameters().get("message"); 29 | message = request.getBody().orElse(message); 30 | context.getLogger().info("Java Http trigger received Message:" + message +" messages for Kafka Output"); 31 | output.setValue(message); 32 | return request.createResponseBuilder(HttpStatus.OK).body(message).build(); 33 | } 34 | 35 | @FunctionName("KafkaTriggerAndKafkaOutput") 36 | public void KafkaTriggerAndKafkaOutput( 37 | @KafkaTrigger( 38 | name = "kafkaTriggerAndKafkaOutput", 39 | topic = "ci", 40 | brokerList="%BrokerList%", 41 | consumerGroup="$Default", 42 | username = "%ConfluentCloudUsername%", 43 | password = "%ConfluentCloudPassword%", 44 | authenticationMode = BrokerAuthenticationMode.PLAIN, 45 | protocol = BrokerProtocol.SASLSSL, 46 | sslCaLocation = "confluent_cloud_cacert.pem", 47 | dataType = "string" 48 | ) String message, 49 | @QueueOutput(name = "output", queueName = "test-kafka-output-cardinality-one-java", connection = "AzureWebJobsStorage") OutputBinding output, 50 | final ExecutionContext context) { 51 | context.getLogger().info("Java Kafka Output function processed a message: " + message); 52 | output.setValue(message); 53 | } 54 | } -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/ServiceBusQueueTriggerTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.microsoft.azure.functions.*; 5 | import java.util.*; 6 | 7 | /** 8 | * Azure Functions with Azure Service Bus Queue. 9 | */ 10 | public class ServiceBusQueueTriggerTests { 11 | 12 | @FunctionName("ServiceBusQueueTrigger") 13 | public void serviceBusQueueTrigger( 14 | @ServiceBusQueueTrigger(name = "message", queueName = "SBQueueNameSingle", connection = "AzureWebJobsServiceBus") String message, 15 | @QueueOutput(name = "output", queueName = "test-servicebusqueuesingle-java", connection = "AzureWebJobsStorage") OutputBinding output, 16 | final ExecutionContext context 17 | ) { 18 | context.getLogger().info("Java Service Bus Queue trigger function processed a message: " + message); 19 | output.setValue(message); 20 | } 21 | 22 | @FunctionName("ServiceBusQueueBatchTrigger") 23 | public void serviceBusQueueBatchTrigger( 24 | @ServiceBusQueueTrigger(name = "message", queueName = "SBQueueNameBatch", connection = "AzureWebJobsServiceBus", cardinality = Cardinality.MANY, dataType = "String") String[] messages, 25 | @QueueOutput(name = "output", queueName = "test-servicebusqueuebatch-java", connection = "AzureWebJobsStorage") OutputBinding output, 26 | final ExecutionContext context 27 | ) { 28 | context.getLogger().info("Java Service Bus Queue trigger function processed a message: " + messages[0]); 29 | output.setValue(messages[0]); 30 | } 31 | 32 | /** 33 | * This function will be invoked when a http request is received. The message contents are provided as output to this function. 34 | */ 35 | @FunctionName("ServiceBusQueueOutput") 36 | public void serviceBusQueueOutput( 37 | @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 38 | @ServiceBusQueueOutput(name = "output", queueName = "%SBQueueName%", connection = "AzureWebJobsServiceBus") OutputBinding output, 39 | final ExecutionContext context 40 | ) { 41 | String message = request.getBody().orElse("default message"); 42 | output.setValue(message); 43 | context.getLogger().info("Java Service Bugs Queue output function got a message: " + message); 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/ServiceBusTopicTriggerTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.microsoft.azure.functions.*; 5 | import java.util.*; 6 | 7 | /** 8 | * Azure Functions with Azure Service Bus Topic. 9 | */ 10 | public class ServiceBusTopicTriggerTests { 11 | 12 | @FunctionName("ServiceBusTopicTrigger") 13 | public void serviceBusTopicTrigger( 14 | @ServiceBusTopicTrigger(name = "message", topicName = "SBTopicNameSingle", subscriptionName="SBTopicNameSingleSubName",connection = "AzureWebJobsServiceBus") String message, 15 | @QueueOutput(name = "output", queueName = "test-servicebustopicbatch-java", connection = "AzureWebJobsStorage") OutputBinding output, 16 | final ExecutionContext context 17 | ) { 18 | context.getLogger().info("Java Service Bus Topic trigger function processed a message: " + message); 19 | output.setValue(message); 20 | } 21 | 22 | @FunctionName("ServiceBusTopicBatchTrigger") 23 | public void serviceBusTopicBatchTrigger( 24 | @ServiceBusTopicTrigger(name = "message", topicName = "SBTopicNameBatch", subscriptionName="SBTopicNameBatchSubName",connection = "AzureWebJobsServiceBus", cardinality = Cardinality.MANY, dataType = "String") List messages, 25 | @QueueOutput(name = "output", queueName = "test-servicebustopicbatch-java", connection = "AzureWebJobsStorage") OutputBinding output, 26 | final ExecutionContext context 27 | ) { 28 | context.getLogger().info("Java Service Bus Topic trigger function processed a message: " + messages.get(0)); 29 | output.setValue(messages.get(0)); 30 | } 31 | 32 | /** 33 | * This function will be invoked when a http request is received. The message contents are provided as output to this function. 34 | */ 35 | @FunctionName("ServiceBusTopicOutput") 36 | public void serviceBusTopicOutput( 37 | @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 38 | @ServiceBusTopicOutput(name = "message", topicName = "%SBTopicName%", subscriptionName="%SBTopicSubName%",connection = "AzureWebJobsServiceBus") OutputBinding output, 39 | final ExecutionContext context 40 | ) { 41 | String message = request.getBody().orElse("default message"); 42 | output.setValue(message); 43 | context.getLogger().info("Java Service Bus Topic output function got a message: " + message); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/SqlTriggerTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.google.gson.Gson; 5 | import com.microsoft.azure.functions.*; 6 | import com.microsoft.azure.functions.HttpMethod; 7 | import com.microsoft.azure.functions.sql.annotation.CommandType; 8 | import com.microsoft.azure.functions.sql.annotation.SQLInput; 9 | import com.microsoft.azure.functions.sql.annotation.SQLOutput; 10 | import com.microsoft.azure.functions.sql.annotation.SQLTrigger; 11 | 12 | import java.io.IOException; 13 | import java.lang.reflect.Array; 14 | import java.util.*; 15 | 16 | /** 17 | * Azure Functions with Azure SQL DB. 18 | */ 19 | public class SqlTriggerTests { 20 | 21 | @FunctionName("GetProducts") 22 | public HttpResponseMessage GetProducts(@HttpTrigger(name = "req", methods = { HttpMethod.GET, 23 | HttpMethod.POST }, route = "getproducts/{productid}", authLevel = AuthorizationLevel.ANONYMOUS) 24 | HttpRequestMessage> request, 25 | @SQLInput(name = "products", commandText = "SELECT TOP 1 * FROM Products WHERE ProductId = @ProductId", 26 | commandType = CommandType.Text, parameters = "@ProductId={productid}", 27 | connectionStringSetting = "AzureWebJobsSqlConnectionString") Product[] products, 28 | final ExecutionContext context) { 29 | 30 | context.getLogger().info("Java HTTP trigger processed a request."); 31 | 32 | if (products.length != 0) { 33 | return request.createResponseBuilder(HttpStatus.OK).body(products[0].toString()).build(); 34 | } else { 35 | return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) 36 | .body("Did not find expected product in table Products").build(); 37 | } 38 | } 39 | 40 | @FunctionName("AddProduct") 41 | public HttpResponseMessage AddProduct(@HttpTrigger(name = "req", methods = { HttpMethod.GET, 42 | HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 43 | @SQLOutput(name = "product", commandText = "Products", connectionStringSetting = "AzureWebJobsSqlConnectionString") OutputBinding product, 44 | final ExecutionContext context) { 45 | context.getLogger().info("Java HTTP trigger processed a request."); 46 | 47 | String json = request.getBody().get(); 48 | product.setValue(new Gson().fromJson(json, Product.class)); 49 | 50 | return request.createResponseBuilder(HttpStatus.OK).body(product).build(); 51 | } 52 | 53 | public class Product { 54 | public int ProductId; 55 | public String Name; 56 | public int Cost; 57 | 58 | public String toString() { 59 | return "{\"ProductId\":" + ProductId + ",\"Name\":\"" + Name + "\",\"Cost\":" + Cost + "}"; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/TableTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.microsoft.azure.functions.*; 5 | import java.util.*; 6 | 7 | /** 8 | * Azure Functions with Azure Storage table. 9 | */ 10 | public class TableTests { 11 | /** 12 | * This function will be invoked when a new queue message is received. 13 | */ 14 | @FunctionName("TableInput") 15 | public void tableInputJava( 16 | @QueueTrigger(name = "message", queueName = "mytablequeue", connection = "AzureWebJobsStorage") String message, 17 | @TableInput(name = "personEntity", tableName = "Person", rowKey = "{queueTrigger}", partitionKey = "firstPartition", connection = "AzureWebJobsStorage") String personEntity, 18 | final ExecutionContext context 19 | ) { 20 | context.getLogger().info("Java Queue trigger function processed a new message: " + message); 21 | context.getLogger().info("Java Table Input function processed a Person entity:" + personEntity); 22 | } 23 | 24 | /** 25 | * This function will be invoked when a new http request is received at the specified path. 26 | */ 27 | @FunctionName("TableOutput") 28 | public void tableOutputJava( 29 | @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 30 | @TableOutput(name = "myOutputTable", tableName = "Person", connection = "AzureWebJobsStorage") OutputBinding myOutputTable, 31 | final ExecutionContext context 32 | ) { 33 | String httpbody = request.getBody().orElse("default"); 34 | myOutputTable.setValue(new Person(httpbody + "Partition", httpbody + "Row", httpbody + "Name")); 35 | context.getLogger().info("Java Table Output function write a new entity into table Person with name: " + httpbody + "Name"); 36 | } 37 | 38 | public static class Person { 39 | public String PartitionKey; 40 | public String RowKey; 41 | public String Name; 42 | 43 | public Person(String p, String r, String n) { 44 | this.PartitionKey = p; 45 | this.RowKey = r; 46 | this.Name = n; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/TimerTriggerTests.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend; 2 | 3 | import com.microsoft.azure.functions.annotation.*; 4 | import com.microsoft.azure.functions.*; 5 | import java.time.*; 6 | 7 | /** 8 | * Azure Functions with Timer trigger. 9 | */ 10 | public class TimerTriggerTests { 11 | /** 12 | * This function will be invoked periodically according to the specified schedule. 13 | */ 14 | @FunctionName("TimerTrigger") 15 | public void timerHandler( 16 | @TimerTrigger(name = "timerInfo", schedule = "0 */5 * * * *") String timerInfo, 17 | final ExecutionContext context 18 | ) { 19 | context.getLogger().info("Java Timer trigger function executed at: " + LocalDateTime.now()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/springcloud/Config.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend.springcloud; 2 | 3 | import com.microsoft.azure.functions.ExecutionContext; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.function.json.JsonMapper; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.messaging.Message; 9 | import reactor.core.publisher.Flux; 10 | import reactor.core.publisher.Mono; 11 | 12 | import java.util.Map; 13 | import java.util.function.Function; 14 | 15 | @SpringBootApplication 16 | public class Config { 17 | 18 | public static void main(String[] args) throws Exception { 19 | SpringApplication.run(Config.class, args); 20 | } 21 | 22 | @Bean 23 | public Function echo() { 24 | return payload -> payload; 25 | } 26 | 27 | @Bean 28 | public Function, String> uppercase() { 29 | return message -> { 30 | String value = message.getPayload(); 31 | ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext"); 32 | if(context != null) 33 | context.getLogger().info(new StringBuilder().append("Function: ") 34 | .append(context.getFunctionName()).append(" is uppercasing ").append(value.toString()).toString()); 35 | return value.toUpperCase(); 36 | }; 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/springcloud/SpringCloudHandler.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.endtoend.springcloud; 2 | 3 | import com.microsoft.azure.functions.ExecutionContext; 4 | import com.microsoft.azure.functions.HttpMethod; 5 | import com.microsoft.azure.functions.HttpRequestMessage; 6 | import com.microsoft.azure.functions.annotation.AuthorizationLevel; 7 | import com.microsoft.azure.functions.annotation.FunctionName; 8 | import com.microsoft.azure.functions.annotation.HttpTrigger; 9 | 10 | import java.util.Optional; 11 | 12 | import org.springframework.cloud.function.adapter.azure.FunctionInvoker; 13 | import org.springframework.messaging.Message; 14 | import org.springframework.messaging.support.MessageBuilder; 15 | 16 | 17 | public class SpringCloudHandler extends FunctionInvoker, String> { 18 | 19 | @FunctionName("echo") 20 | public String echo(@HttpTrigger(name = "req", methods = {HttpMethod.GET, 21 | HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 22 | ExecutionContext context) { 23 | context.getLogger().info("Java Spring Cloud echo function processed a request."); 24 | String query = request.getQueryParameters().get("name"); 25 | query = query != null ? query : "echo"; 26 | Message message = MessageBuilder.withPayload(request.getBody().orElse(query)).copyHeaders(request.getHeaders()).build(); 27 | return handleRequest(message, context); 28 | } 29 | 30 | @FunctionName("uppercase") 31 | public String uppercase( 32 | @HttpTrigger( 33 | name = "req", 34 | methods = {HttpMethod.GET, HttpMethod.POST}, 35 | authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 36 | ExecutionContext context 37 | ) { 38 | context.getLogger().info("Java Spring Cloud uppercase function processed a request."); 39 | String query = request.getQueryParameters().get("name"); 40 | query = query != null ? query : "uppercase"; 41 | Message message = MessageBuilder.withPayload(request.getBody().orElse(query)) 42 | .copyHeaders(request.getHeaders()).build(); 43 | return handleRequest(message, context); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /endtoendtests/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /eng/ci/code-mirror.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: 4 | - dev 5 | - release/* # azure-functions-java-worker github repo restricts creation of release/* branches, so using a pattern is safe here. 6 | 7 | resources: 8 | repositories: 9 | - repository: eng 10 | type: git 11 | name: engineering 12 | ref: refs/tags/release 13 | 14 | variables: 15 | - template: ci/variables/cfs.yml@eng 16 | 17 | extends: 18 | template: ci/code-mirror.yml@eng -------------------------------------------------------------------------------- /eng/ci/integration-tests.yml: -------------------------------------------------------------------------------- 1 | trigger: none # ensure this is not ran as a CI build 2 | 3 | pr: 4 | branches: 5 | include: 6 | - dev 7 | - release/* 8 | 9 | resources: 10 | repositories: 11 | - repository: 1es 12 | type: git 13 | name: 1ESPipelineTemplates/1ESPipelineTemplates 14 | ref: refs/tags/release 15 | - repository: eng 16 | type: git 17 | name: engineering 18 | ref: refs/tags/release 19 | 20 | variables: 21 | - template: ci/variables/build.yml@eng 22 | - template: /ci/variables/cfs.yml@eng 23 | 24 | extends: 25 | template: v1/1ES.Unofficial.PipelineTemplate.yml@1es 26 | parameters: 27 | pool: 28 | name: 1es-pool-azfunc 29 | image: 1es-windows-2022 30 | os: windows 31 | 32 | stages: 33 | - stage: TestWindows 34 | jobs: 35 | - template: /eng/ci/templates/official/jobs/run-e2e-tests-windows.yml@self 36 | 37 | - stage: TestLinux 38 | dependsOn: 39 | - TestWindows 40 | 41 | jobs: 42 | - template: /eng/ci/templates/official/jobs/run-e2e-tests-linux.yml@self -------------------------------------------------------------------------------- /eng/ci/official-build.yml: -------------------------------------------------------------------------------- 1 | schedules: 2 | - cron: '0 20 * * *' 3 | displayName: Nightly Build 4 | branches: 5 | include: 6 | - dev 7 | always: true 8 | 9 | trigger: 10 | batch: true 11 | branches: 12 | include: 13 | - dev 14 | - release/* # azure-functions-java-worker github repo restricts creation of release/* branches, so using a pattern is safe here. 15 | 16 | # CI only, does not trigger on PRs. 17 | pr: none 18 | 19 | resources: 20 | repositories: 21 | - repository: 1es 22 | type: git 23 | name: 1ESPipelineTemplates/1ESPipelineTemplates 24 | ref: refs/tags/release 25 | - repository: eng 26 | type: git 27 | name: engineering 28 | ref: refs/tags/release 29 | 30 | variables: 31 | - template: ci/variables/build.yml@eng 32 | - template: ci/variables/cfs.yml@eng 33 | - name: codeql.language 34 | value: java,powershell,csharp 35 | - name: codeql.buildIdentifier 36 | value: java_worker_official 37 | - name: codeql.excludePathPatterns 38 | value: extract/inst 39 | 40 | extends: 41 | template: v1/1ES.Official.PipelineTemplate.yml@1es 42 | parameters: 43 | pool: 44 | name: 1es-pool-azfunc 45 | image: 1es-windows-2022 46 | os: windows 47 | 48 | stages: 49 | - stage: Build 50 | jobs: 51 | - template: /eng/ci/templates/official/jobs/build-artifacts.yml@self 52 | 53 | - stage: TestWindows 54 | dependsOn: [] 55 | jobs: 56 | - template: /eng/ci/templates/jobs/run-emulated-tests-windows.yml@self 57 | parameters: 58 | poolName: 1es-pool-azfunc 59 | 60 | - stage: TestLinux 61 | dependsOn: [] 62 | jobs: 63 | - template: /eng/ci/templates/jobs/run-emulated-tests-linux.yml@self 64 | parameters: 65 | poolName: 1es-pool-azfunc 66 | -------------------------------------------------------------------------------- /eng/ci/public-build.yml: -------------------------------------------------------------------------------- 1 | schedules: 2 | - cron: '0 0 * * *' 3 | displayName: Nightly Build 4 | branches: 5 | include: 6 | - dev 7 | always: true 8 | 9 | trigger: 10 | batch: true 11 | branches: 12 | include: 13 | - dev 14 | 15 | pr: 16 | branches: 17 | include: 18 | - dev 19 | 20 | resources: 21 | repositories: 22 | - repository: 1es 23 | type: git 24 | name: 1ESPipelineTemplates/1ESPipelineTemplates 25 | ref: refs/tags/release 26 | 27 | parameters: 28 | - name: runSdkTypesTests 29 | type: boolean 30 | default: false 31 | 32 | extends: 33 | template: v1/1ES.Unofficial.PipelineTemplate.yml@1es 34 | parameters: 35 | pool: 36 | name: 1es-pool-azfunc-public 37 | image: 1es-windows-2022 38 | os: windows 39 | 40 | sdl: 41 | codeql: 42 | compiled: 43 | enabled: true # still only runs for default branch 44 | language: java,powershell,csharp 45 | buildIdentifier: java_worker_public 46 | excludePathPatterns: extract/inst 47 | 48 | settings: 49 | # PR's from forks do not have sufficient permissions to set tags. 50 | skipBuildTagsForGitHubPullRequests: ${{ variables['System.PullRequest.IsFork'] }} 51 | 52 | stages: 53 | - stage: Build 54 | jobs: 55 | - template: /eng/ci/templates/jobs/build.yml@self 56 | 57 | - stage: TestWindows 58 | dependsOn: [] 59 | jobs: 60 | - template: /eng/ci/templates/jobs/run-emulated-tests-windows.yml@self 61 | parameters: 62 | poolName: 1es-pool-azfunc-public 63 | runSdkTypesTests: ${{ parameters.runSdkTypesTests }} 64 | 65 | - stage: TestLinux 66 | dependsOn: [] 67 | jobs: 68 | - template: /eng/ci/templates/jobs/run-emulated-tests-linux.yml@self 69 | parameters: 70 | poolName: 1es-pool-azfunc-public 71 | runSdkTypesTests: ${{ parameters.runSdkTypesTests }} 72 | -------------------------------------------------------------------------------- /eng/ci/templates/jobs/build.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: "Build" 3 | displayName: 'Build java worker' 4 | 5 | pool: 6 | name: 1es-pool-azfunc-public 7 | image: 1es-windows-2022 8 | os: windows 9 | 10 | steps: 11 | - pwsh: | 12 | Get-Command mvn 13 | displayName: 'Check Maven is installed' 14 | - pwsh: | 15 | java -version 16 | displayName: 'Check default java version' 17 | - pwsh: | 18 | .\installAdditionsLocally.ps1 19 | displayName: 'Install java-additions locally' 20 | - pwsh: | 21 | mvn clean package 22 | displayName: 'Build java worker' -------------------------------------------------------------------------------- /eng/ci/templates/official/jobs/build-artifacts.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: "Build" 3 | displayName: 'Build java worker' 4 | 5 | templateContext: 6 | outputParentDirectory: $(Build.ArtifactStagingDirectory) 7 | outputs: 8 | - output: pipelineArtifact 9 | targetPath: $(Build.ArtifactStagingDirectory) 10 | artifactName: 'drop' 11 | - output: nuget 12 | condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/dev'), eq(variables['UPLOADPACKAGETOPRERELEASEFEED'], true)) 13 | useDotNetTask: false 14 | packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg' 15 | packageParentPath: '$(Build.ArtifactStagingDirectory)' 16 | publishVstsFeed: 'e6a70c92-4128-439f-8012-382fe78d6396/f37f760c-aebd-443e-9714-ce725cd427df' 17 | nuGetFeedType: 'internal' 18 | allowPackageConflicts: true 19 | 20 | pool: 21 | name: 1es-pool-azfunc 22 | image: 1es-windows-2022 23 | os: windows 24 | 25 | variables: 26 | ${{ if contains(variables['Build.SourceBranch'], 'refs/heads/release/' ) }}: 27 | isReleaseTemp: true 28 | isRelease: $[variables.isReleaseTemp] 29 | 30 | steps: 31 | - task: NuGetToolInstaller@1 32 | inputs: 33 | checkLatest: true 34 | displayName: 'Install NuGet Tool' 35 | - pwsh: | 36 | Get-Command mvn 37 | displayName: 'Check Maven is installed' 38 | - pwsh: | 39 | java -version 40 | displayName: 'Check default java version' 41 | - pwsh: | 42 | .\installAdditionsLocally.ps1 43 | displayName: 'Install java-additions locally' 44 | - pwsh: | 45 | if ("$(isRelease)"){ 46 | $buildNumber="$(Build.SourceBranchName)" 47 | Write-Host "Triggered for release." 48 | } 49 | else { 50 | $buildNumber="$(Build.BuildNumber)-v4" 51 | Write-Host "Not triggered for release. Setting package suffix to '$buildNumber'" 52 | } 53 | Write-Host "##vso[task.setvariable variable=buildNumber;isOutput=true;]$buildNumber" 54 | .\package-pipeline.ps1 -buildNumber $buildNumber 55 | name: output 56 | displayName: 'Executing build script' 57 | - task: CopyFiles@2 58 | inputs: 59 | SourceFolder: '$(Build.Repository.LocalPath)/pkg' 60 | Contents: '*.nupkg' 61 | TargetFolder: '$(Build.ArtifactStagingDirectory)' 62 | CleanTargetFolder: true 63 | displayName: 'Copying files for artifacts' -------------------------------------------------------------------------------- /installAdditionsLocally.ps1: -------------------------------------------------------------------------------- 1 | # Variables for first repository 2 | $repoUrl1 = 'https://github.com/Azure/azure-functions-java-additions.git' 3 | $branchName1 = 'dev' 4 | $repoName1 = 'azure-functions-java-additions' 5 | 6 | # Clone the first repository 7 | git clone $repoUrl1 8 | 9 | # Change directory to the cloned repository 10 | Set-Location $repoName1 11 | 12 | # Checkout the desired branch 13 | git checkout $branchName1 14 | 15 | # Detect OS and execute build accordingly 16 | if ($IsWindows) { 17 | # Run the batch script (mvnBuild.bat) 18 | & "..\mvnBuildAdditions.bat" 19 | } else { 20 | # Extract and explicitly invoke the mvn command from mvnBuild.bat 21 | $mvnCommand = Get-Content "../mvnBuildAdditions.bat" | Where-Object { $_ -match '^mvn\s+' } 22 | if ($null -ne $mvnCommand) { 23 | # Execute the extracted mvn command explicitly as a single line 24 | bash -c "$mvnCommand" 25 | } else { 26 | Write-Error "No mvn command found in mvnBuild.bat." 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /installMavenPluginLocally.ps1: -------------------------------------------------------------------------------- 1 | # Variables for second repository 2 | $repoUrl2 = 'https://github.com/ahmedmuhsin/azure-maven-plugins.git' 3 | $branchName2 = 'sdk-types' 4 | $repoName2 = 'azure-maven-plugins' 5 | 6 | # Clone the second repository 7 | git clone $repoUrl2 8 | 9 | # Change directory to the cloned repository 10 | Set-Location $repoName2 11 | 12 | # Checkout the desired branch 13 | git checkout $branchName2 14 | 15 | # Run Maven command to build/install, skipping tests and javadoc 16 | if ($IsWindows) { 17 | & "mvn" "clean" "install" "-DskipTests" "-Dmaven.javadoc.skip=true" 18 | } else { 19 | bash -c "mvn clean install -DskipTests -Dmaven.javadoc.skip=true" 20 | } 21 | -------------------------------------------------------------------------------- /mvnBuild.bat: -------------------------------------------------------------------------------- 1 | mvn clean package -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -B -------------------------------------------------------------------------------- /mvnBuildAdditions.bat: -------------------------------------------------------------------------------- 1 | mvn clean install -U -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -B -Dgpg.skip -Dspotbugs.skip=true -------------------------------------------------------------------------------- /mvnBuildSkipTests.bat: -------------------------------------------------------------------------------- 1 | mvn clean package -Dmaven.javadoc.skip=true -Dmaven.test.skip -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -B -------------------------------------------------------------------------------- /samples/dependency-injection-example/README.md: -------------------------------------------------------------------------------- 1 | ## Dependency Injection Examples for Azure Java Functions 2 | 3 | This rep contains examples that integrate Google Dagger2 and Google Juice with Azure Java Functions. 4 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/README.md: -------------------------------------------------------------------------------- 1 | # azure-function-guice 2 | Integration Google Dagger2 with Azure Functions 3 | 4 | Utilize [com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector](https://github.com/Azure/azure-functions-java-additions/blob/dev/azure-functions-java-spi/src/main/java/com/microsoft/azure/functions/spi/inject/FunctionInstanceInjector.java) provided by azure functions java worker to integrate Google Dagger2 framework into Azure Java Function. 5 | 6 | ## Local Setup 7 | 1. Clone the repo 8 | 2. Enter corresponding directory and run `mvn clean package` to build the project 9 | 3. Run `mvn azure-functions:run` to run the project on local. 10 | Local example: 11 | 12 | ![img.png](img.png) 13 | 14 | ![img_1.png](img_1.png) 15 | 4. Run `mvn azure-functions:deploy` to deploy the function app, for more info about deploy azure function java app please refer to [deploy java function app](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser#deploy-the-function-project-to-azure) -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "extensionBundle": { 4 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 5 | "version": "[3.*, 4.0.0)" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/samples/dependency-injection-example/dagger-function/img.png -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/samples/dependency-injection-example/dagger-function/img_1.png -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/src/main/java/com/azfs/Function.java: -------------------------------------------------------------------------------- 1 | package com.azfs; 2 | 3 | import com.azfs.model.Communicator; 4 | import com.microsoft.azure.functions.ExecutionContext; 5 | import com.microsoft.azure.functions.HttpMethod; 6 | import com.microsoft.azure.functions.HttpRequestMessage; 7 | import com.microsoft.azure.functions.HttpResponseMessage; 8 | import com.microsoft.azure.functions.HttpStatus; 9 | import com.microsoft.azure.functions.annotation.AuthorizationLevel; 10 | import com.microsoft.azure.functions.annotation.FunctionName; 11 | import com.microsoft.azure.functions.annotation.HttpTrigger; 12 | 13 | import javax.inject.Inject; 14 | import java.util.Optional; 15 | 16 | /** 17 | * Azure Functions with HTTP Trigger. 18 | */ 19 | public class Function { 20 | /** 21 | * This function listens at endpoint "/api/HttpExample". Two ways to invoke it using "curl" command in bash: 22 | * 1. curl -d "HTTP Body" {your host}/api/HttpExample 23 | * 2. curl "{your host}/api/HttpExample?name=HTTP%20Query" 24 | */ 25 | 26 | private final Communicator communicator; 27 | 28 | @Inject 29 | public Function(Communicator communicator) { 30 | this.communicator = communicator; 31 | } 32 | 33 | @FunctionName("HttpExample") 34 | public HttpResponseMessage run( 35 | @HttpTrigger( 36 | name = "req", 37 | methods = {HttpMethod.GET, HttpMethod.POST}, 38 | authLevel = AuthorizationLevel.ANONYMOUS) 39 | HttpRequestMessage> request, 40 | final ExecutionContext context) { 41 | context.getLogger().info("Java HTTP trigger processed a request."); 42 | 43 | // Parse query parameter 44 | final String query = request.getQueryParameters().get("name"); 45 | final String name = request.getBody().orElse(query); 46 | 47 | communicator.communicate(context); 48 | 49 | if (name == null) { 50 | return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build(); 51 | } else { 52 | return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/src/main/java/com/azfs/component/FunctionComponent.java: -------------------------------------------------------------------------------- 1 | package com.azfs.component; 2 | 3 | import com.azfs.Function; 4 | import com.azfs.module.FunctionModule; 5 | import dagger.Component; 6 | 7 | @Component(modules = FunctionModule.class) 8 | public interface FunctionComponent { 9 | Function buildFunction(); 10 | } 11 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/src/main/java/com/azfs/dihook/MyFunctionInstanceInjector.java: -------------------------------------------------------------------------------- 1 | package com.azfs.dihook; 2 | 3 | import com.azfs.component.DaggerFunctionComponent; 4 | import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector; 5 | 6 | public class MyFunctionInstanceInjector implements FunctionInstanceInjector { 7 | @Override 8 | public T getInstance(Class aClass) throws Exception { 9 | return (T) DaggerFunctionComponent.create().buildFunction(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/src/main/java/com/azfs/model/Communicator.java: -------------------------------------------------------------------------------- 1 | package com.azfs.model; 2 | 3 | import com.microsoft.azure.functions.ExecutionContext; 4 | 5 | public class Communicator { 6 | private final String id; 7 | 8 | public Communicator(String id) { 9 | this.id = id; 10 | } 11 | 12 | public void communicate(ExecutionContext context){ 13 | context.getLogger().info("Message sent out from injected communicator :) "); 14 | //add your own logics ... 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/src/main/java/com/azfs/module/FunctionModule.java: -------------------------------------------------------------------------------- 1 | package com.azfs.module; 2 | 3 | import com.azfs.model.Communicator; 4 | import dagger.Module; 5 | import dagger.Provides; 6 | 7 | @Module 8 | public class FunctionModule { 9 | @Provides 10 | public Communicator provideCommunicator() { 11 | return new Communicator("123"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/dagger-function/src/main/resources/META-INF/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector: -------------------------------------------------------------------------------- 1 | com.azfs.dihook.MyFunctionInstanceInjector -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/.gitignore: -------------------------------------------------------------------------------- 1 | # Build output 2 | target/ 3 | *.class 4 | 5 | # Log file 6 | *.log 7 | 8 | # BlueJ files 9 | *.ctxt 10 | 11 | # Mobile Tools for Java (J2ME) 12 | .mtj.tmp/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # IDE 26 | .idea/ 27 | *.iml 28 | .settings/ 29 | .project 30 | .classpath 31 | .vscode/ 32 | 33 | # macOS 34 | .DS_Store 35 | 36 | # Azure Functions 37 | local.settings.json 38 | bin/ 39 | obj/ 40 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/README.md: -------------------------------------------------------------------------------- 1 | # azure-function-guice 2 | Integration Google Guice with Azure Functions 3 | 4 | Utilize [com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector](https://github.com/Azure/azure-functions-java-additions/blob/dev/azure-functions-java-spi/src/main/java/com/microsoft/azure/functions/spi/inject/FunctionInstanceInjector.java) provided by azure functions java worker to integrate Google Guice framework into Azure Java Function. 5 | 6 | ## Local Setup 7 | 1. Clone the repo 8 | 2. Enter corresponding directory and run `mvn clean package` to build the project 9 | 3. Run `mvn azure-functions:run` to run the project on local. 10 | Local example: 11 | 12 | ![img.png](img.png) 13 | 14 | ![img_2.png](img_2.png) 15 | 4. Run `mvn azure-functions:deploy` to deploy the function app, for more info about deploy azure function java app please refer to [deploy java function app](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser#deploy-the-function-project-to-azure) -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "extensionBundle": { 4 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 5 | "version": "[3.*, 4.0.0)" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/samples/dependency-injection-example/guice-function/img.png -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/samples/dependency-injection-example/guice-function/img_2.png -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/src/main/java/com/azfs/BasicModule.java: -------------------------------------------------------------------------------- 1 | package com.azfs; 2 | 3 | import com.google.inject.AbstractModule; 4 | 5 | public class BasicModule extends AbstractModule { 6 | 7 | @Override 8 | protected void configure() { 9 | bind(Communicator.class).to(DefaultCommunicatorImpl.class); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/src/main/java/com/azfs/Communicator.java: -------------------------------------------------------------------------------- 1 | package com.azfs; 2 | 3 | import com.microsoft.azure.functions.ExecutionContext; 4 | 5 | public interface Communicator { 6 | boolean sendMessage(ExecutionContext context); 7 | } 8 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/src/main/java/com/azfs/DefaultCommunicatorImpl.java: -------------------------------------------------------------------------------- 1 | package com.azfs; 2 | 3 | import com.microsoft.azure.functions.ExecutionContext; 4 | 5 | public class DefaultCommunicatorImpl implements Communicator{ 6 | @Override 7 | public boolean sendMessage(ExecutionContext context) { 8 | context.getLogger().info("Message sent out from injected communicator :) "); 9 | //add your own logic... 10 | return true; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/src/main/java/com/azfs/Function.java: -------------------------------------------------------------------------------- 1 | package com.azfs; 2 | 3 | import com.google.inject.Inject; 4 | import com.microsoft.azure.functions.ExecutionContext; 5 | import com.microsoft.azure.functions.HttpMethod; 6 | import com.microsoft.azure.functions.HttpRequestMessage; 7 | import com.microsoft.azure.functions.HttpResponseMessage; 8 | import com.microsoft.azure.functions.HttpStatus; 9 | import com.microsoft.azure.functions.annotation.AuthorizationLevel; 10 | import com.microsoft.azure.functions.annotation.FunctionName; 11 | import com.microsoft.azure.functions.annotation.HttpTrigger; 12 | 13 | import java.util.Optional; 14 | 15 | /** 16 | * Azure Functions with HTTP Trigger. 17 | */ 18 | public class Function { 19 | /** 20 | * This function listens at endpoint "/api/HttpExample". Two ways to invoke it using "curl" command in bash: 21 | * 1. curl -d "HTTP Body" {your host}/api/HttpExample 22 | * 2. curl "{your host}/api/HttpExample?name=HTTP%20Query" 23 | */ 24 | 25 | @Inject 26 | public Communicator communicator; 27 | 28 | @FunctionName("HttpExample") 29 | public HttpResponseMessage run( 30 | @HttpTrigger( 31 | name = "req", 32 | methods = {HttpMethod.GET, HttpMethod.POST}, 33 | authLevel = AuthorizationLevel.ANONYMOUS) 34 | HttpRequestMessage> request, 35 | final ExecutionContext context) { 36 | context.getLogger().info("Java HTTP trigger processed a request."); 37 | 38 | // Parse query parameter 39 | final String query = request.getQueryParameters().get("name"); 40 | final String name = request.getBody().orElse(query); 41 | 42 | //use the injected communicator to send out message 43 | communicator.sendMessage(context); 44 | 45 | if (name == null) { 46 | return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build(); 47 | } else { 48 | return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/src/main/java/com/azfs/dihook/FunctionGuiceFactory.java: -------------------------------------------------------------------------------- 1 | package com.azfs.dihook; 2 | 3 | import com.google.inject.Guice; 4 | import com.azfs.BasicModule; 5 | import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector; 6 | 7 | public class FunctionGuiceFactory implements FunctionInstanceInjector { 8 | @Override 9 | public T getInstance(Class functionClass) throws Exception { 10 | return Guice.createInjector(new BasicModule()).getInstance(functionClass); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/dependency-injection-example/guice-function/src/main/resources/META-INF/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector: -------------------------------------------------------------------------------- 1 | com.azfs.dihook.FunctionGuiceFactory -------------------------------------------------------------------------------- /samples/spring-cloud-example/.gitignore: -------------------------------------------------------------------------------- 1 | # Build output 2 | target/ 3 | *.class 4 | 5 | # Log file 6 | *.log 7 | 8 | # BlueJ files 9 | *.ctxt 10 | 11 | # Mobile Tools for Java (J2ME) 12 | .mtj.tmp/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # IDE 26 | .idea/ 27 | *.iml 28 | .settings/ 29 | .project 30 | .classpath 31 | .vscode/ 32 | 33 | # macOS 34 | .DS_Store 35 | 36 | # Azure Functions 37 | local.settings.json 38 | bin/ 39 | obj/ 40 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/main/java/example/Application.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import example.uppercase.Config; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | public class Application { 9 | public static void main(String[] args) throws Exception { 10 | SpringApplication.run(Config.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/main/java/example/hello/Hello.java: -------------------------------------------------------------------------------- 1 | package example.hello; 2 | 3 | import example.hello.model.*; 4 | import org.springframework.stereotype.Component; 5 | import java.util.function.Function; 6 | 7 | @Component 8 | public class Hello implements Function { 9 | 10 | @Override 11 | public Greeting apply(User user) { 12 | return new Greeting("Hello, " + user.getName() + "!\n"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/main/java/example/hello/HelloHandler.java: -------------------------------------------------------------------------------- 1 | package example.hello; 2 | 3 | import com.microsoft.azure.functions.*; 4 | import com.microsoft.azure.functions.annotation.AuthorizationLevel; 5 | import com.microsoft.azure.functions.annotation.FunctionName; 6 | import com.microsoft.azure.functions.annotation.HttpTrigger; 7 | import example.hello.model.*; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.Optional; 12 | 13 | @Component 14 | public class HelloHandler { 15 | 16 | @Autowired 17 | private Hello hello; 18 | 19 | @FunctionName("hello") 20 | public HttpResponseMessage execute( 21 | @HttpTrigger(name = "request", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 22 | ExecutionContext context) { 23 | User user = request.getBody() 24 | .filter(u -> u.getName() != null) 25 | .orElseGet(() -> new User(request.getQueryParameters().getOrDefault("name", "world"))); 26 | context.getLogger().info("Greeting user name: " + user.getName()); 27 | return request 28 | .createResponseBuilder(HttpStatus.OK) 29 | .body(hello.apply(user)) 30 | .header("Content-Type", "application/json") 31 | .build(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/main/java/example/hello/model/Greeting.java: -------------------------------------------------------------------------------- 1 | package example.hello.model; 2 | 3 | public class Greeting { 4 | 5 | private String message; 6 | 7 | public Greeting() { 8 | } 9 | 10 | public Greeting(String message) { 11 | this.message = message; 12 | } 13 | 14 | public String getMessage() { 15 | return message; 16 | } 17 | 18 | public void setMessage(String message) { 19 | this.message = message; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/main/java/example/hello/model/User.java: -------------------------------------------------------------------------------- 1 | package example.hello.model; 2 | 3 | public class User { 4 | 5 | private String name; 6 | 7 | public User() { 8 | } 9 | 10 | public User(String name) { 11 | this.name = name; 12 | } 13 | 14 | public String getName() { 15 | return name; 16 | } 17 | 18 | public void setName(String name) { 19 | this.name = name; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/main/java/example/uppercase/Config.java: -------------------------------------------------------------------------------- 1 | package example.uppercase; 2 | 3 | import java.util.Map; 4 | import java.util.function.Function; 5 | 6 | import com.microsoft.azure.functions.ExecutionContext; 7 | import org.springframework.context.annotation.Configuration; 8 | import reactor.core.publisher.Flux; 9 | import reactor.core.publisher.Mono; 10 | 11 | import org.springframework.boot.SpringApplication; 12 | import org.springframework.boot.autoconfigure.SpringBootApplication; 13 | import org.springframework.cloud.function.json.JsonMapper; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.messaging.Message; 16 | 17 | @Configuration 18 | public class Config { 19 | 20 | @Bean 21 | public Function echo() { 22 | return payload -> payload; 23 | } 24 | 25 | @Bean 26 | public Function uppercase() { 27 | return payload -> payload.toUpperCase(); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/main/java/example/uppercase/EchoHandler.java: -------------------------------------------------------------------------------- 1 | package example.uppercase; 2 | 3 | import com.microsoft.azure.functions.ExecutionContext; 4 | import com.microsoft.azure.functions.HttpMethod; 5 | import com.microsoft.azure.functions.HttpRequestMessage; 6 | import com.microsoft.azure.functions.annotation.AuthorizationLevel; 7 | import com.microsoft.azure.functions.annotation.FunctionName; 8 | import com.microsoft.azure.functions.annotation.HttpTrigger; 9 | 10 | import java.util.Optional; 11 | import java.util.function.Function; 12 | 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.cloud.function.adapter.azure.FunctionInvoker; 15 | import org.springframework.messaging.Message; 16 | import org.springframework.messaging.support.MessageBuilder; 17 | import org.springframework.stereotype.Component; 18 | 19 | @Component 20 | public class EchoHandler { 21 | 22 | @Autowired 23 | private Function echo; 24 | 25 | @FunctionName("echo") 26 | public String execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET, 27 | HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 28 | ExecutionContext context) { 29 | return echo.apply(request.getBody().get()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/main/java/example/uppercase/UppercaseHandler.java: -------------------------------------------------------------------------------- 1 | package example.uppercase; 2 | 3 | import com.microsoft.azure.functions.ExecutionContext; 4 | import com.microsoft.azure.functions.HttpMethod; 5 | import com.microsoft.azure.functions.HttpRequestMessage; 6 | import com.microsoft.azure.functions.annotation.AuthorizationLevel; 7 | import com.microsoft.azure.functions.annotation.FunctionName; 8 | import com.microsoft.azure.functions.annotation.HttpTrigger; 9 | 10 | import java.util.Optional; 11 | import java.util.function.Function; 12 | 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.cloud.function.adapter.azure.FunctionInvoker; 15 | import org.springframework.messaging.Message; 16 | import org.springframework.messaging.support.MessageBuilder; 17 | import org.springframework.stereotype.Component; 18 | 19 | @Component 20 | public class UppercaseHandler { 21 | 22 | @Autowired 23 | private Function uppercase; 24 | 25 | @FunctionName("uppercase") 26 | public String execute( 27 | @HttpTrigger( 28 | name = "req", 29 | methods = {HttpMethod.GET, HttpMethod.POST}, 30 | authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 31 | ExecutionContext context 32 | ) { 33 | context.getLogger().warning("Using Java (" + System.getProperty("java.version") + ")"); 34 | return uppercase.apply(request.getBody().get()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/main/resources/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "extensionBundle": { 4 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 5 | "version": "[4.*, 5.0.0)" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /samples/spring-cloud-example/src/test/java/example/hello/HelloTest.java: -------------------------------------------------------------------------------- 1 | package example.hello; 2 | 3 | 4 | import example.hello.model.Greeting; 5 | import example.hello.model.User; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | 11 | public class HelloTest { 12 | 13 | @Test 14 | public void test() { 15 | Greeting result = new Hello().apply(new User("foo")); 16 | assertThat(result.getMessage()).isEqualTo("Hello, foo!\n"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /setup-tests-pipeline.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) Microsoft. All rights reserved. 3 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | # 5 | param 6 | ( 7 | [Switch]$UseCoreToolsBuildFromIntegrationTests 8 | ) 9 | 10 | $FUNC_RUNTIME_VERSION = 'latest' 11 | 12 | Write-Host "Installing Core Tools globlally using npm, version: $FUNC_RUNTIME_VERSION ..." 13 | 14 | $FUNC_CLI_DIRECTORY = Join-Path $PSScriptRoot 'Azure.Functions.Cli' 15 | $InstallDir = $FUNC_CLI_DIRECTORY 16 | 17 | # 1. Clean previous install 18 | Remove-Item -Recurse -Force $InstallDir -ErrorAction Ignore 19 | New-Item -ItemType Directory -Path $InstallDir -ErrorAction Ignore 20 | 21 | # 2. Locate the global prefix and module root that npm just used 22 | $globalPrefix = (npm prefix -g | Out-String).Trim() # e.g. /usr/local or C:\Users\\AppData\Roaming\npm 23 | $globalNode = (npm root -g | Out-String).Trim() # e.g. /usr/local/lib/node_modules 24 | $moduleRoot = Join-Path $globalNode 'azure-functions-core-tools' 25 | 26 | # 3. npm install → temp folder 27 | npm install -g azure-functions-core-tools@$FUNC_RUNTIME_VERSION --unsafe-perm true --foreground-scripts --loglevel verbose 28 | 29 | # 4. Copy CLI payload into the layout required by your tests 30 | Copy-Item "$moduleRoot\bin\*" $InstallDir -Recurse -Force 31 | 32 | 33 | if (-not $UseCoreToolsBuildFromIntegrationTests.IsPresent) 34 | { 35 | Write-Host "Replacing Java worker binaries in the Core Tools..." 36 | Get-ChildItem -Path "$PSScriptRoot/target/*" -Include 'azure*' -Exclude '*shaded.jar','*tests.jar' | ForEach-Object { 37 | Copy-Item $_.FullName "$FUNC_CLI_DIRECTORY/workers/java/azure-functions-java-worker.jar" -Force -Verbose 38 | } 39 | 40 | Write-Host "Copying worker.config.json to worker directory" 41 | Copy-Item "$PSScriptRoot/worker.config.json" "$FUNC_CLI_DIRECTORY/workers/java" -Force -Verbose 42 | Write-Host "Copying worker.config.json and annotationLib to worker directory" 43 | Copy-Item "$PSScriptRoot/annotationLib" "$FUNC_CLI_DIRECTORY/workers/java" -Recurse -Verbose -Force 44 | Write-Host "Copying the unsigned Application Insights Agent to worker directory" 45 | Copy-Item "$PSScriptRoot/agent" "$FUNC_CLI_DIRECTORY/workers/java" -Recurse -Verbose -Force 46 | } 47 | -------------------------------------------------------------------------------- /src/main/azure-functions-language-worker-protobuf/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/about-codeowners/ 2 | # for more info about CODEOWNERS file 3 | # 4 | # It uses the same pattern rule for gitignore file 5 | # https://git-scm.com/docs/gitignore#_pattern_format 6 | 7 | 8 | 9 | # AZURE FUNCTIONS TEAM 10 | # For all file changes, github would automatically 11 | # include the following people in the PRs. 12 | # Language owners should get notified of any new changes to the proto file. 13 | 14 | src/proto/FunctionRpc.proto @vrdmr @gavin-aguiar @YunchuWang @surgupta-msft @satvu @ejizba @alrod @anatolib @kaibocai @shreyas-gopalakrishna @amamounelsayed @Francisco-Gamino 15 | -------------------------------------------------------------------------------- /src/main/azure-functions-language-worker-protobuf/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /src/main/azure-functions-language-worker-protobuf/src/proto/identity/ClaimsIdentityRpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | // protobuf vscode extension: https://marketplace.visualstudio.com/items?itemName=zxh404.vscode-proto3 3 | 4 | option java_package = "com.microsoft.azure.functions.rpc.messages"; 5 | 6 | import "shared/NullableTypes.proto"; 7 | 8 | // Light-weight representation of a .NET System.Security.Claims.ClaimsIdentity object. 9 | // This is the same serialization as found in EasyAuth, and needs to be kept in sync with 10 | // its ClaimsIdentitySlim definition, as seen in the WebJobs extension: 11 | // https://github.com/Azure/azure-webjobs-sdk-extensions/blob/dev/src/WebJobs.Extensions.Http/ClaimsIdentitySlim.cs 12 | message RpcClaimsIdentity { 13 | NullableString authentication_type = 1; 14 | NullableString name_claim_type = 2; 15 | NullableString role_claim_type = 3; 16 | repeated RpcClaim claims = 4; 17 | } 18 | 19 | // Light-weight representation of a .NET System.Security.Claims.Claim object. 20 | // This is the same serialization as found in EasyAuth, and needs to be kept in sync with 21 | // its ClaimSlim definition, as seen in the WebJobs extension: 22 | // https://github.com/Azure/azure-webjobs-sdk-extensions/blob/dev/src/WebJobs.Extensions.Http/ClaimSlim.cs 23 | message RpcClaim { 24 | string value = 1; 25 | string type = 2; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/azure-functions-language-worker-protobuf/src/proto/shared/NullableTypes.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | // protobuf vscode extension: https://marketplace.visualstudio.com/items?itemName=zxh404.vscode-proto3 3 | 4 | option java_package = "com.microsoft.azure.functions.rpc.messages"; 5 | 6 | import "google/protobuf/timestamp.proto"; 7 | 8 | message NullableString { 9 | oneof string { 10 | string value = 1; 11 | } 12 | } 13 | 14 | message NullableDouble { 15 | oneof double { 16 | double value = 1; 17 | } 18 | } 19 | 20 | message NullableBool { 21 | oneof bool { 22 | bool value = 1; 23 | } 24 | } 25 | 26 | message NullableTimestamp { 27 | oneof timestamp { 28 | google.protobuf.Timestamp value = 1; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/Constants.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker; 2 | 3 | /** 4 | * The Constants file for Java language worker 5 | */ 6 | public final class Constants { 7 | private Constants(){} 8 | 9 | public final static String FUNCTIONS_URI_OPTION = "functions-uri"; 10 | public final static String FUNCTIONS_WORKER_ID_OPTION = "functions-worker-id"; 11 | public final static String FUNCTIONS_REQUEST_ID_OPTION = "functions-request-id"; 12 | public final static String FUNCTIONS_GRPC_MAX_MESSAGE_LENGTH_OPTION = "functions-grpc-max-message-length"; 13 | public final static String FUNCTIONS_CONSOLE_LOG_OPTION = "functions-console-log"; 14 | public final static String TRIGGER_METADATA_DOLLAR_REQUEST_KEY = "$request"; 15 | public final static String JAVA_LIBRARY_DIRECTORY = "/annotationLib"; 16 | public final static String JAVA_LIBRARY_ARTIFACT_ID = "azure-functions-java-library"; 17 | public final static String HAS_IMPLICIT_OUTPUT_QUALIFIED_NAME = "com.microsoft.azure.functions.annotation.HasImplicitOutput"; 18 | public static final String JAVA_ENABLE_OPENTELEMETRY = "JAVA_ENABLE_OPENTELEMETRY"; 19 | public static final String JAVA_APPLICATIONINSIGHTS_ENABLE_TELEMETRY = "JAVA_APPLICATIONINSIGHTS_ENABLE_TELEMETRY"; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/IApplication.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker; 2 | 3 | public interface IApplication { 4 | boolean logToConsole(); 5 | String getHost(); 6 | int getPort(); 7 | Integer getMaxMessageSize(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/Util.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker; 2 | 3 | public class Util { 4 | public static boolean isTrue(String value) { 5 | if(value != null && (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("1"))) { 6 | return true; 7 | } 8 | return false; 9 | } 10 | 11 | public static String getJavaVersion() { 12 | return String.join(" - ", System.getProperty("java.home"), System.getProperty("java.version")); 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/BindingData.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import org.apache.commons.lang3.*; 4 | 5 | /** 6 | * Provides the information such as the matching level of the actual value retrieved/converted from BindingDataStore. 7 | */ 8 | public final class BindingData { 9 | public BindingData(Object value) { 10 | this.value = value; 11 | } 12 | 13 | Object getNullSafeValue() { return this.value; } 14 | public Object getValue() { return this.getNullSafeValue() == ObjectUtils.NULL ? null : this.getNullSafeValue(); } 15 | 16 | private final Object value; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/BindingDefinition.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import com.microsoft.azure.functions.rpc.messages.*; 4 | 5 | public final class BindingDefinition { 6 | public BindingDefinition(String name, BindingInfo info) { 7 | this.name = name; 8 | this.direction = info.getDirection(); 9 | } 10 | 11 | String getName() { return this.name; } 12 | boolean isInput() { return this.direction == BindingInfo.Direction.in || this.direction == BindingInfo.Direction.inout; } 13 | boolean isOutput() { return this.direction == BindingInfo.Direction.out || this.direction == BindingInfo.Direction.inout; } 14 | 15 | private final String name; 16 | private final BindingInfo.Direction direction; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/DataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import java.lang.reflect.Type; 4 | import java.lang.reflect.TypeVariable; 5 | import java.util.Map; 6 | import java.util.Optional; 7 | 8 | import org.apache.commons.lang3.ObjectUtils; 9 | import org.apache.commons.lang3.exception.ExceptionUtils; 10 | import org.apache.commons.lang3.reflect.TypeUtils; 11 | 12 | /** 13 | * Base class of all data sources. Provides basic information and logic for type 14 | * conversion. Data operation template: T (source) -> Object (value). 15 | * Thread-safety: Single thread. 16 | */ 17 | abstract class DataSource { 18 | DataSource(String name, T value, DataOperations operations) { 19 | this.name = name; 20 | this.value = value; 21 | this.operations = operations; 22 | } 23 | 24 | T getValue() { 25 | return this.value; 26 | } 27 | 28 | void setValue(T value) { 29 | this.value = value; 30 | } 31 | 32 | public Optional computeByName(String name, Type target) { 33 | Optional> source = this.lookupName(name); 34 | if (!source.isPresent()) { 35 | if (target.equals(Optional.class)) { 36 | return Optional.of(new BindingData(Optional.empty())); 37 | } 38 | return Optional.empty(); 39 | } 40 | Optional data; 41 | try { 42 | data = source.get().computeByType(target); 43 | return data; 44 | } catch (Exception ex) { 45 | ExceptionUtils.rethrow(ex); 46 | } 47 | return Optional.empty(); 48 | } 49 | 50 | public Optional computeByType(Type target) { 51 | boolean isTargetOptional = Optional.class.equals(TypeUtils.getRawType(target, null)); 52 | if (isTargetOptional) { 53 | Map, Type> typeArgs = TypeUtils.getTypeArguments(target, Optional.class); 54 | target = typeArgs.size() > 0 ? typeArgs.values().iterator().next() : Object.class; 55 | } 56 | return this.operations.apply(this.value, target).map(obj -> { 57 | if (isTargetOptional) { 58 | if (obj == ObjectUtils.NULL) { 59 | obj = null; 60 | } 61 | obj = Optional.ofNullable(obj); 62 | } 63 | return new BindingData(obj); 64 | }); 65 | } 66 | 67 | protected Optional> lookupName(String name) { 68 | return Optional.ofNullable(this.name != null && this.name.equals(name) ? this : null); 69 | } 70 | 71 | private final String name; 72 | private T value; 73 | private final DataOperations operations; 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/DataTarget.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import java.util.Optional; 4 | 5 | import com.microsoft.azure.functions.OutputBinding; 6 | import com.microsoft.azure.functions.rpc.messages.TypedData; 7 | import com.microsoft.azure.functions.rpc.messages.TypedData.Builder; 8 | 9 | /** 10 | * Base class of all output data sources. The type conversion logic is just the 11 | * opposite of the normal input data source. Data operation template: Object 12 | * (source) -> TypedData.Builder. Thread-safety: Single thread. 13 | */ 14 | abstract class DataTarget implements OutputBinding { 15 | DataTarget(DataOperations dataOperations) { 16 | this.dataOperations = dataOperations; 17 | } 18 | 19 | public Optional computeFromValue() throws Exception { 20 | if (this.value == null) { 21 | return Optional.of(TypedData.newBuilder().setJson("null").build()); 22 | } 23 | 24 | Optional data; 25 | 26 | data = this.dataOperations.applyTypeAssignment(this.value, this.value.getClass()).map(TypedData.Builder::build); 27 | if (data.isPresent()) { 28 | return data; 29 | } 30 | 31 | return Optional.empty(); 32 | } 33 | 34 | @Override 35 | public Object getValue() { 36 | return this.value; 37 | } 38 | 39 | @Override 40 | public void setValue(Object value) { 41 | this.value = value; 42 | } 43 | 44 | private Object value; 45 | private DataOperations dataOperations; 46 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/ExecutionRetryContext.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import com.microsoft.azure.functions.RetryContext; 4 | import com.microsoft.azure.functions.RpcException; 5 | 6 | final public class ExecutionRetryContext implements RetryContext { 7 | public ExecutionRetryContext(int retryCount, int maxRetryCount, RpcException rpcException) { 8 | this.Retrycount = retryCount; 9 | this.Maxretrycount = maxRetryCount; 10 | this.Rpcexception = rpcException; 11 | } 12 | 13 | public ExecutionRetryContext(int retryCount, int maxRetryCount, com.microsoft.azure.functions.rpc.messages.RpcException exception) { 14 | this.Retrycount = retryCount; 15 | this.Maxretrycount = maxRetryCount; 16 | this.Rpcexception = new RpcException() { 17 | @Override 18 | public String getSource() { 19 | return exception.getSource(); 20 | } 21 | 22 | @Override 23 | public String getStacktrace() { 24 | return exception.getStackTrace(); 25 | } 26 | 27 | @Override 28 | public String getMessage() { 29 | return exception.getMessage(); 30 | } 31 | }; 32 | } 33 | 34 | @Override 35 | public int getRetrycount() { 36 | return Retrycount; 37 | } 38 | 39 | @Override 40 | public int getMaxretrycount() { 41 | return Maxretrycount; 42 | } 43 | 44 | @Override 45 | public RpcException getException() { 46 | return Rpcexception; 47 | } 48 | 49 | private final int Retrycount; 50 | private final int Maxretrycount; 51 | private final RpcException Rpcexception; 52 | 53 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/ExecutionTraceContext.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import java.util.Map; 4 | 5 | import com.microsoft.azure.functions.TraceContext; 6 | 7 | final public class ExecutionTraceContext implements TraceContext { 8 | public ExecutionTraceContext(String traceParent, String traceState, Map attributes) { 9 | this.Attributes = attributes; 10 | this.Traceparent = traceParent; 11 | this.Tracestate = traceState; 12 | } 13 | 14 | @Override 15 | public String getTraceparent() { return this.Traceparent; } 16 | 17 | @Override 18 | public String getTracestate() { return this.Tracestate; } 19 | 20 | @Override 21 | public Map getAttributes() { return this.Attributes; } 22 | 23 | private final String Traceparent; 24 | private final String Tracestate; 25 | private final Map Attributes; 26 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcByteArrayDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import org.apache.commons.lang3.ArrayUtils; 4 | 5 | import com.google.protobuf.ByteString; 6 | 7 | final public class RpcByteArrayDataSource extends DataSource { 8 | public RpcByteArrayDataSource(String name, ByteString value) { 9 | super(name, value.toByteArray(), BYTE_ARRAY_DATA_OPERATIONS); 10 | } 11 | 12 | private static final DataOperations BYTE_ARRAY_DATA_OPERATIONS = new DataOperations<>(); 13 | static { 14 | BYTE_ARRAY_DATA_OPERATIONS.addOperation(Byte[].class, ArrayUtils::toObject); 15 | BYTE_ARRAY_DATA_OPERATIONS.addOperation(byte[].class, ArrayUtils::clone); 16 | BYTE_ARRAY_DATA_OPERATIONS.addGenericOperation(String.class, (v,t) -> new String(v)); 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionByteArrayDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import com.google.protobuf.ByteString; 4 | import com.microsoft.azure.functions.rpc.messages.CollectionBytes; 5 | import org.apache.commons.lang3.ArrayUtils; 6 | 7 | import java.lang.reflect.ParameterizedType; 8 | import java.lang.reflect.Type; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | public final class RpcCollectionByteArrayDataSource extends DataSource> { 14 | public RpcCollectionByteArrayDataSource(String name, CollectionBytes value) { 15 | super(name, value.getBytesList(), COLLECTION_DATA_OPERATIONS); 16 | } 17 | private static final DataOperations, Object> COLLECTION_DATA_OPERATIONS = new DataOperations<>(); 18 | 19 | public static Object convertToByteList(List sourceValue, Type targetType) { 20 | if(targetType == List.class) { 21 | return sourceValue.stream().map(element -> element.toByteArray()).collect(Collectors.toCollection(ArrayList::new)); 22 | } 23 | else if(targetType instanceof ParameterizedType){ 24 | Type targetActualType = ((ParameterizedType) targetType).getActualTypeArguments()[0]; 25 | if (targetActualType == byte[].class) { 26 | return sourceValue.stream().map(element -> element.toByteArray()).collect(Collectors.toCollection(ArrayList::new)); 27 | } else if (targetActualType == Byte[].class) { 28 | return sourceValue.stream().map(element -> (ArrayUtils.toObject(element.toByteArray()))).collect(Collectors.toList()); 29 | } 30 | throw new UnsupportedOperationException("Input data type \"" + targetActualType + "\" is not supported"); 31 | } 32 | throw new UnsupportedOperationException("Input data type \"" + targetType + "\" is not supported"); 33 | } 34 | 35 | public static Object convertToByteListDefault(List sourceValue, Type targetType) { 36 | return sourceValue.stream().map(element -> element.toByteArray()).collect(Collectors.toCollection(ArrayList::new)); 37 | } 38 | 39 | public static Object convertToBytesArray(List sourceValue, Type targetType) { 40 | return sourceValue.stream().map(element -> element.toByteArray()).collect(Collectors.toCollection(ArrayList::new)).toArray(new byte[0][]); 41 | } 42 | 43 | public static Object convertToBytesObjectArray(List sourceValue, Type targetType) { 44 | return sourceValue.stream().map(element -> (ArrayUtils.toObject(element.toByteArray()))).collect(Collectors.toCollection(ArrayList::new)).toArray(new Byte[0][]); 45 | } 46 | 47 | static { 48 | COLLECTION_DATA_OPERATIONS.addGenericOperation(List.class, (v, t) -> convertToByteList(v, t)); 49 | COLLECTION_DATA_OPERATIONS.addGenericOperation(byte[][].class, (v, t) -> convertToBytesArray(v, t)); 50 | COLLECTION_DATA_OPERATIONS.addGenericOperation(Byte[][].class, (v, t) -> convertToBytesObjectArray(v, t)); 51 | COLLECTION_DATA_OPERATIONS.addGenericOperation(String.class, (v, t) -> convertToByteListDefault(v, t)); 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionDoubleDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import com.microsoft.azure.functions.rpc.messages.CollectionDouble; 4 | 5 | import java.lang.reflect.ParameterizedType; 6 | import java.lang.reflect.Type; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public final class RpcCollectionDoubleDataSource extends DataSource> { 11 | public RpcCollectionDoubleDataSource(String name, CollectionDouble value) { 12 | super(name, value.getDoubleList(), COLLECTION_DATA_OPERATIONS); 13 | } 14 | private static final DataOperations, Object> COLLECTION_DATA_OPERATIONS = new DataOperations<>(); 15 | 16 | public static Object convertToDoubleListDefault(List sourceValue, Type targetType) { 17 | return new ArrayList<>(sourceValue); 18 | } 19 | 20 | public static Object convertToDoubleList(List sourceValue, Type targetType) { 21 | if(targetType == List.class) { 22 | return new ArrayList<>(sourceValue); 23 | } 24 | else if(targetType instanceof ParameterizedType) { 25 | Type targetActualType = ((ParameterizedType) targetType).getActualTypeArguments()[0]; 26 | if (targetActualType == Double.class) { 27 | return new ArrayList<>(sourceValue); 28 | } 29 | throw new UnsupportedOperationException("Input data type \"" + targetActualType + "\" is not supported"); 30 | } 31 | throw new UnsupportedOperationException("Input data type \"" + targetType + "\" is not supported"); 32 | } 33 | 34 | public static Object convertToDoubleObjectArray(List sourceValue, Type targetType) { 35 | return new ArrayList<>(sourceValue).toArray(new Double[0]); 36 | } 37 | 38 | public static Object convertToDoubleArray(List sourceValue, Type targetType) { 39 | return sourceValue.stream().mapToDouble(Double::doubleValue).toArray(); 40 | } 41 | 42 | static { 43 | COLLECTION_DATA_OPERATIONS.addGenericOperation(List.class, (v, t) -> convertToDoubleList(v, t)); 44 | COLLECTION_DATA_OPERATIONS.addGenericOperation(Double[].class, (v, t) -> convertToDoubleObjectArray(v, t)); 45 | COLLECTION_DATA_OPERATIONS.addGenericOperation(double[].class, (v, t) -> convertToDoubleArray(v, t)); 46 | COLLECTION_DATA_OPERATIONS.addGenericOperation(String.class, (v, t) -> convertToDoubleListDefault(v, t)); 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionLongDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import com.microsoft.azure.functions.rpc.messages.CollectionSInt64; 4 | 5 | import java.lang.reflect.ParameterizedType; 6 | import java.lang.reflect.Type; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public final class RpcCollectionLongDataSource extends DataSource> { 11 | public RpcCollectionLongDataSource(String name, CollectionSInt64 value) { 12 | super(name, value.getSint64List(), COLLECTION_DATA_OPERATIONS); 13 | } 14 | private static final DataOperations, Object> COLLECTION_DATA_OPERATIONS = new DataOperations<>(); 15 | 16 | public static Object convertToLongList(List sourceValue, Type targetType) { 17 | if(targetType == List.class) { 18 | return new ArrayList<>(sourceValue); 19 | } 20 | else if(targetType instanceof ParameterizedType) { 21 | Type targetActualType = ((ParameterizedType) targetType).getActualTypeArguments()[0]; 22 | if (targetActualType == Long.class) { 23 | return new ArrayList<>(sourceValue); 24 | } 25 | throw new UnsupportedOperationException("Input data type \"" + targetActualType + "\" is not supported"); 26 | } 27 | throw new UnsupportedOperationException("Input data type \"" + targetType + "\" is not supported"); 28 | } 29 | 30 | public static Object convertToLongListDefault(List sourceValue, Type targetType) { 31 | return new ArrayList<>(sourceValue); 32 | } 33 | 34 | public static Object convertToLongObjectArray(List sourceValue, Type targetType) { 35 | return new ArrayList<>(sourceValue).toArray(new Long[0]); 36 | } 37 | 38 | public static Object convertToLongArray(List sourceValue, Type targetType) { 39 | return sourceValue.stream().mapToLong(Long::longValue).toArray(); 40 | } 41 | 42 | static { 43 | COLLECTION_DATA_OPERATIONS.addGenericOperation(List.class, (v, t) -> convertToLongList(v, t)); 44 | COLLECTION_DATA_OPERATIONS.addGenericOperation(Long[].class, (v, t) -> convertToLongObjectArray(v, t)); 45 | COLLECTION_DATA_OPERATIONS.addGenericOperation(long[].class, (v, t) -> convertToLongArray(v, t)); 46 | COLLECTION_DATA_OPERATIONS.addGenericOperation(String.class, (v, t) -> convertToLongListDefault(v, t)); 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionStringDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import com.microsoft.azure.functions.rpc.messages.CollectionString; 4 | 5 | import java.lang.reflect.ParameterizedType; 6 | import java.lang.reflect.Type; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public final class RpcCollectionStringDataSource extends DataSource> { 11 | public RpcCollectionStringDataSource(String name, CollectionString value) { 12 | super(name, value.getStringList(), COLLECTION_DATA_OPERATIONS); 13 | } 14 | private static final DataOperations, Object> COLLECTION_DATA_OPERATIONS = new DataOperations<>(); 15 | 16 | public static Object convertToStringList(List sourceValue, Type targetType) { 17 | if(targetType == List.class) { 18 | return new ArrayList<>(sourceValue); 19 | } 20 | else if(targetType instanceof ParameterizedType) { 21 | Type targetActualType = ((ParameterizedType) targetType).getActualTypeArguments()[0]; 22 | if (targetActualType == String.class) { 23 | return new ArrayList<>(sourceValue); 24 | } 25 | throw new UnsupportedOperationException("Input data type \"" + targetActualType + "\" is not supported"); 26 | } 27 | throw new UnsupportedOperationException("Input data type \"" + targetType + "\" is not supported"); 28 | } 29 | 30 | public static Object convertToStringArray(List sourceValue, Type targetType) { 31 | return new ArrayList<>(sourceValue).toArray(new String[0]); 32 | } 33 | 34 | public static Object convertToStringListDefault(List sourceValue, Type targetType) { 35 | return new ArrayList<>(sourceValue); 36 | } 37 | 38 | static { 39 | COLLECTION_DATA_OPERATIONS.addGenericOperation(List.class, (v, t) -> convertToStringList(v, t)); 40 | COLLECTION_DATA_OPERATIONS.addGenericOperation(String[].class, (v, t) -> convertToStringArray(v, t)); 41 | COLLECTION_DATA_OPERATIONS.addGenericOperation(String.class, (v, t) -> convertToStringListDefault(v, t)); 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcEmptyDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import java.util.*; 4 | 5 | import com.google.protobuf.*; 6 | 7 | import org.apache.commons.lang3.*; 8 | 9 | final class RpcEmptyDataSource extends DataSource { 10 | RpcEmptyDataSource(String name) { super(name, null, EMPTY_DATA_OPERATIONS); } 11 | 12 | private static final DataOperations EMPTY_DATA_OPERATIONS = new DataOperations<>(); 13 | static { 14 | EMPTY_DATA_OPERATIONS.addOperation(Optional.class, v -> Optional.empty()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcHttpDataTarget.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import com.microsoft.azure.functions.HttpResponseMessage; 7 | import com.microsoft.azure.functions.HttpStatus; 8 | import com.microsoft.azure.functions.HttpStatusType; 9 | import com.microsoft.azure.functions.rpc.messages.RpcHttp; 10 | import com.microsoft.azure.functions.rpc.messages.TypedData; 11 | 12 | final class RpcHttpDataTarget extends DataTarget implements HttpResponseMessage, HttpResponseMessage.Builder { 13 | RpcHttpDataTarget() { 14 | super(HTTP_TARGET_OPERATIONS); 15 | this.headers = new HashMap<>(); 16 | this.httpStatus = HttpStatus.OK; 17 | this.httpStatusCode = HttpStatus.OK.value(); 18 | super.setValue(this); 19 | } 20 | 21 | @Override 22 | public HttpStatusType getStatus() { return httpStatus; } 23 | @Override 24 | public int getStatusCode() { return httpStatusCode; } 25 | @Override 26 | public String getHeader(String key) { return this.headers.get(key); } 27 | @Override 28 | public Object getBody() { return this.body; } 29 | 30 | private int httpStatusCode; 31 | private HttpStatusType httpStatus; 32 | private Object body; 33 | private Map headers; 34 | 35 | public static TypedData.Builder toRpcHttpData(RpcHttpDataTarget response) throws Exception { 36 | TypedData.Builder dataBuilder = TypedData.newBuilder(); 37 | if (response != null) { 38 | RpcHttp.Builder httpBuilder = RpcHttp.newBuilder().setStatusCode(Integer.toString(response.getStatusCode())); 39 | response.headers.forEach(httpBuilder::putHeaders); 40 | RpcUnspecifiedDataTarget bodyTarget = new RpcUnspecifiedDataTarget(); 41 | bodyTarget.setValue(response.getBody()); 42 | bodyTarget.computeFromValue().ifPresent(httpBuilder::setBody); 43 | dataBuilder.setHttp(httpBuilder); 44 | } 45 | return dataBuilder; 46 | } 47 | 48 | private static final DataOperations HTTP_TARGET_OPERATIONS = new DataOperations<>(); 49 | static { 50 | HTTP_TARGET_OPERATIONS.addTargetOperation(HttpResponseMessage.class, v -> toRpcHttpData((RpcHttpDataTarget) v)); 51 | HTTP_TARGET_OPERATIONS.addTargetOperation(RpcHttpDataTarget.class, v -> toRpcHttpData((RpcHttpDataTarget) v)); 52 | } 53 | 54 | 55 | public Builder status(HttpStatus status) { 56 | this.httpStatusCode = status.value(); 57 | this.httpStatus = status; 58 | return this; 59 | } 60 | 61 | 62 | @Override 63 | public Builder status(HttpStatusType httpStatusType) { 64 | this.httpStatusCode = httpStatusType.value(); 65 | this.httpStatus = httpStatusType; 66 | return this; 67 | } 68 | 69 | 70 | public Builder status(int httpStatusCode) { 71 | if (httpStatusCode < 100 || httpStatusCode > 599) { 72 | throw new IllegalArgumentException("Invalid HTTP Status code class. Valid classes are in the range of 1xx, 2xx, 3xx, 4xx and 5xx."); 73 | } 74 | this.httpStatusCode = httpStatusCode; 75 | this.httpStatus = HttpStatusType.custom(httpStatusCode); 76 | return this; 77 | } 78 | 79 | 80 | @Override 81 | public Builder header(String key, String value) { 82 | this.headers.put(key, value); 83 | return this; 84 | } 85 | 86 | @Override 87 | public Builder body(Object body) { 88 | this.body = body; 89 | return this; 90 | } 91 | 92 | @Override 93 | public HttpResponseMessage build() { 94 | return this; 95 | } 96 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcHttpRequestDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import java.lang.reflect.Type; 4 | import java.lang.reflect.TypeVariable; 5 | import java.net.URI; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import org.apache.commons.lang3.reflect.TypeUtils; 11 | 12 | import com.microsoft.azure.functions.HttpMethod; 13 | import com.microsoft.azure.functions.HttpRequestMessage; 14 | import com.microsoft.azure.functions.HttpResponseMessage; 15 | import com.microsoft.azure.functions.HttpStatus; 16 | import com.microsoft.azure.functions.HttpStatusType; 17 | import com.microsoft.azure.functions.HttpResponseMessage.Builder; 18 | import com.microsoft.azure.functions.rpc.messages.RpcHttp; 19 | 20 | public final class RpcHttpRequestDataSource extends DataSource { 21 | public RpcHttpRequestDataSource(String name, RpcHttp value) { 22 | super(name, null, HTTP_DATA_OPERATIONS); 23 | this.httpPayload = value; 24 | this.bodyDataSource = BindingDataStore.rpcSourceFromTypedData(null, this.httpPayload.getBody()); 25 | this.fields = Arrays.asList(this.httpPayload.getHeadersMap(), this.httpPayload.getQueryMap(), 26 | this.httpPayload.getParamsMap()); 27 | this.setValue(this); 28 | } 29 | 30 | static class HttpRequestMessageImpl implements HttpRequestMessage { 31 | private HttpRequestMessageImpl(RpcHttpRequestDataSource parentDataSource, Object body) { 32 | this.parentDataSource = parentDataSource; 33 | this.body = body; 34 | } 35 | 36 | @Override 37 | public URI getUri() { 38 | return URI.create(this.parentDataSource.httpPayload.getUrl()); 39 | } 40 | 41 | @Override 42 | public HttpMethod getHttpMethod() { 43 | return HttpMethod.value(this.parentDataSource.httpPayload.getMethod()); 44 | } 45 | 46 | @Override 47 | public Map getHeaders() { 48 | return this.parentDataSource.httpPayload.getHeadersMap(); 49 | } 50 | 51 | @Override 52 | public Map getQueryParameters() { 53 | return this.parentDataSource.httpPayload.getQueryMap(); 54 | } 55 | 56 | @Override 57 | public Object getBody() { 58 | return this.body; 59 | } 60 | 61 | @Override 62 | public HttpResponseMessage.Builder createResponseBuilder(HttpStatusType status) { 63 | return new RpcHttpDataTarget().status(status); 64 | } 65 | 66 | @Override 67 | public Builder createResponseBuilder(HttpStatus status) { 68 | return new RpcHttpDataTarget().status(status); 69 | } 70 | 71 | private RpcHttpRequestDataSource parentDataSource; 72 | private Object body; 73 | 74 | } 75 | 76 | private final RpcHttp httpPayload; 77 | private final DataSource bodyDataSource; 78 | private final List> fields; 79 | 80 | private static final DataOperations HTTP_DATA_OPERATIONS = new DataOperations<>(); 81 | static { 82 | HTTP_DATA_OPERATIONS.addGenericOperation(HttpRequestMessage.class, (v, t) -> { 83 | Map, Type> typeArgs = TypeUtils.getTypeArguments(t, HttpRequestMessage.class); 84 | Type actualType = typeArgs.size() > 0 ? typeArgs.values().iterator().next() : Object.class; 85 | BindingData bodyData = v.bodyDataSource.computeByType(actualType).orElseThrow(ClassCastException::new); 86 | return new HttpRequestMessageImpl(v, bodyData.getValue()); 87 | }); 88 | } 89 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcIntegerDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | public final class RpcIntegerDataSource extends DataSource { 4 | public RpcIntegerDataSource(String name, long value) { 5 | super(name, value, LONG_DATA_OPERATIONS); 6 | } 7 | 8 | private static final DataOperations LONG_DATA_OPERATIONS = new DataOperations<>(); 9 | static { 10 | LONG_DATA_OPERATIONS.addOperation(int.class, Long::intValue); 11 | LONG_DATA_OPERATIONS.addOperation(Integer.class, Long::intValue); 12 | LONG_DATA_OPERATIONS.addOperation(short.class, Long::shortValue); 13 | LONG_DATA_OPERATIONS.addOperation(Short.class, Long::shortValue); 14 | LONG_DATA_OPERATIONS.addOperation(byte.class, Long::byteValue); 15 | LONG_DATA_OPERATIONS.addOperation(Byte.class, Long::byteValue); 16 | LONG_DATA_OPERATIONS.addOperation(String.class, Object::toString); 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcJsonDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import java.lang.reflect.Type; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.List; 7 | 8 | import org.apache.commons.lang3.reflect.TypeUtils; 9 | 10 | import com.google.gson.Gson; 11 | import com.google.gson.JsonArray; 12 | import com.google.gson.JsonParser; 13 | import com.google.gson.JsonSyntaxException; 14 | 15 | public final class RpcJsonDataSource extends DataSource { 16 | public RpcJsonDataSource(String name, String value) { 17 | super(name, value, JSON_DATA_OPERATIONS); 18 | } 19 | 20 | public static final Gson gson = new Gson(); 21 | public static final JsonParser gsonParser = new JsonParser(); 22 | private static final DataOperations JSON_DATA_OPERATIONS = new DataOperations<>(); 23 | 24 | public static Object convertToStringArrayOrList(String sourceValue, Type targetType) { 25 | try { 26 | return gson.fromJson(sourceValue, targetType); 27 | } catch (JsonSyntaxException ex) { 28 | List jsonStringArrayList = new ArrayList(); 29 | JsonArray array = gsonParser.parse(sourceValue).getAsJsonArray(); 30 | for (int i = 0; i < array.size(); i++) { 31 | jsonStringArrayList.add(array.get(i).toString()); 32 | } 33 | if (Collection.class.isAssignableFrom(TypeUtils.getRawType(targetType, null))) { 34 | return jsonStringArrayList; 35 | } 36 | String[] jsonStringListAsArray = new String[jsonStringArrayList.size()]; 37 | jsonStringListAsArray = jsonStringArrayList.toArray(jsonStringListAsArray); 38 | return jsonStringListAsArray; 39 | } 40 | } 41 | 42 | static { 43 | JSON_DATA_OPERATIONS.addOperation(String.class, s -> s); 44 | JSON_DATA_OPERATIONS.addGenericOperation(String[].class, (v, t) -> convertToStringArrayOrList(v, t)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcRealNumberDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | final class RpcRealNumberDataSource extends DataSource { 4 | RpcRealNumberDataSource(String name, double value) { super(name, value, REALNUMBER_DATA_OPERATIONS); } 5 | 6 | static final DataOperations REALNUMBER_DATA_OPERATIONS = new DataOperations<>(); 7 | static { 8 | REALNUMBER_DATA_OPERATIONS.addOperation(float.class, Double::floatValue); 9 | REALNUMBER_DATA_OPERATIONS.addOperation(Float.class, Double::floatValue); 10 | REALNUMBER_DATA_OPERATIONS.addOperation(String.class, Object::toString); 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/binding/RpcStringDataSource.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | public final class RpcStringDataSource extends DataSource { 6 | public RpcStringDataSource(String name, String value) { super(name, value, STRING_DATA_OPERATIONS); } 7 | 8 | private static Object convertToJson(boolean isStrict, String s, Type target) throws ClassCastException { 9 | DataSource jsonSource = new RpcJsonDataSource(null, s); 10 | if (isStrict) { 11 | return jsonSource.computeByType(target).orElseThrow(ClassCastException::new).getNullSafeValue(); 12 | } else { 13 | return jsonSource.computeByType(target).orElseThrow(ClassCastException::new).getNullSafeValue(); 14 | } 15 | } 16 | 17 | static final DataOperations STRING_DATA_OPERATIONS = new DataOperations<>(); 18 | static { 19 | STRING_DATA_OPERATIONS.addOperation(long.class, Long::parseLong); 20 | STRING_DATA_OPERATIONS.addOperation(Long.class, Long::parseLong); 21 | STRING_DATA_OPERATIONS.addOperation(int.class, Integer::parseInt); 22 | STRING_DATA_OPERATIONS.addOperation(Integer.class, Integer::parseInt); 23 | STRING_DATA_OPERATIONS.addOperation(short.class, Short::parseShort); 24 | STRING_DATA_OPERATIONS.addOperation(Short.class, Short::parseShort); 25 | STRING_DATA_OPERATIONS.addOperation(byte.class, Byte::parseByte); 26 | STRING_DATA_OPERATIONS.addOperation(Byte.class, Byte::parseByte); 27 | STRING_DATA_OPERATIONS.addOperation(double.class, Double::parseDouble); 28 | STRING_DATA_OPERATIONS.addOperation(Double.class, Double::parseDouble); 29 | STRING_DATA_OPERATIONS.addOperation(float.class, Float::parseFloat); 30 | STRING_DATA_OPERATIONS.addOperation(Float.class, Float::parseFloat); 31 | STRING_DATA_OPERATIONS.addGenericOperation(String.class, (v, t) -> convertToJson(false, v, t)); 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/broker/EnhancedJavaMethodExecutorImpl.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker; 2 | 3 | import com.microsoft.azure.functions.worker.binding.*; 4 | 5 | /** 6 | * Used to executor of arbitrary Java method in any JAR using reflection. 7 | * Thread-Safety: Multiple thread. 8 | */ 9 | public class EnhancedJavaMethodExecutorImpl implements JavaMethodExecutor { 10 | 11 | private final ClassLoader classLoader; 12 | 13 | public EnhancedJavaMethodExecutorImpl(ClassLoader classLoader) { 14 | this.classLoader = classLoader; 15 | } 16 | 17 | public void execute(ExecutionContextDataSource executionContextDataSource) throws Exception { 18 | try { 19 | Thread.currentThread().setContextClassLoader(this.classLoader); 20 | Object retValue = ParameterResolver.resolveArguments(executionContextDataSource) 21 | .orElseThrow(() -> new NoSuchMethodException("Cannot locate the method signature with the given input")) 22 | .invoke(executionContextDataSource::getFunctionInstance); 23 | executionContextDataSource.updateReturnValue(retValue); 24 | } finally { 25 | Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader()); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/broker/FunctionDefinition.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker; 2 | 3 | import com.microsoft.azure.functions.rpc.messages.BindingInfo; 4 | import com.microsoft.azure.functions.worker.binding.BindingDefinition; 5 | import com.microsoft.azure.functions.worker.description.FunctionMethodDescriptor; 6 | import com.microsoft.azure.functions.worker.reflect.ClassLoaderProvider; 7 | 8 | import java.lang.reflect.Method; 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * This Class encapsulate data about functions defined by customer. 16 | * Each function defined by customer will be used to build one FunctionDefinition object accordingly. 17 | */ 18 | public class FunctionDefinition { 19 | private final Class containingClass; 20 | private final MethodBindInfo candidate; 21 | private final Map bindingDefinitions; 22 | private final FunctionMethodDescriptor descriptor; 23 | 24 | public FunctionDefinition(FunctionMethodDescriptor descriptor, Map bindingInfos, ClassLoaderProvider classLoaderProvider) 25 | throws ClassNotFoundException, NoSuchMethodException 26 | { 27 | this.descriptor = descriptor; 28 | this.descriptor.validateMethodInfo(); 29 | 30 | this.containingClass = getContainingClass(descriptor.getFullClassName(), classLoaderProvider); 31 | 32 | this.candidate = getCandidate(descriptor, containingClass); 33 | 34 | this.bindingDefinitions = new HashMap<>(); 35 | 36 | for (Map.Entry entry : bindingInfos.entrySet()) { 37 | this.bindingDefinitions.put(entry.getKey(), new BindingDefinition(entry.getKey(), entry.getValue())); 38 | } 39 | } 40 | 41 | private static Class getContainingClass(String className, ClassLoaderProvider classLoaderProvider) throws ClassNotFoundException { 42 | ClassLoader classLoader = classLoaderProvider.createClassLoader(); 43 | return Class.forName(className, false, classLoader); 44 | } 45 | 46 | private static MethodBindInfo getCandidate(FunctionMethodDescriptor descriptor, Class containingClass) throws NoSuchMethodException { 47 | List candidates = new ArrayList<>(); 48 | for (Method method : containingClass.getMethods()) { 49 | if (method.getDeclaringClass().getName().equals(descriptor.getFullClassName()) && method.getName().equals(descriptor.getMethodName())) { 50 | candidates.add(new MethodBindInfo(method)); 51 | } 52 | } 53 | 54 | if (candidates.isEmpty()) { 55 | throw new NoSuchMethodException("There are no methods named \"" + descriptor.getName() + "\" in class \"" + descriptor.getFullClassName() + "\""); 56 | } 57 | 58 | if (candidates.size() > 1) { 59 | throw new UnsupportedOperationException("Found more than one function with method name \"" + descriptor.getName() + "\" in class \"" + descriptor.getFullClassName() + "\""); 60 | } 61 | 62 | return candidates.get(0); 63 | } 64 | 65 | public Class getContainingClass() { 66 | return containingClass; 67 | } 68 | 69 | public Map getBindingDefinitions() { 70 | return bindingDefinitions; 71 | } 72 | 73 | public MethodBindInfo getCandidate() { 74 | return candidate; 75 | } 76 | 77 | public FunctionMethodDescriptor getDescriptor() { return descriptor; } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodExecutor.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker; 2 | 3 | 4 | import java.util.*; 5 | import com.microsoft.azure.functions.worker.binding.*; 6 | 7 | 8 | /** 9 | * Used to executor of arbitrary Java method in any JAR using reflection. 10 | * Thread-Safety: Multiple thread. 11 | */ 12 | public interface JavaMethodExecutor { 13 | void execute(ExecutionContextDataSource executionContextDataSource) throws Exception; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodExecutorImpl.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker; 2 | 3 | import com.microsoft.azure.functions.worker.binding.*; 4 | 5 | /** 6 | * Used to executor of arbitrary Java method in any JAR using reflection. 7 | * Thread-Safety: Multiple thread. 8 | */ 9 | public class JavaMethodExecutorImpl implements JavaMethodExecutor { 10 | 11 | private static final JavaMethodExecutorImpl INSTANCE = new JavaMethodExecutorImpl(); 12 | 13 | public static JavaMethodExecutorImpl getInstance(){ 14 | return INSTANCE; 15 | } 16 | 17 | private JavaMethodExecutorImpl () {} 18 | 19 | public void execute(ExecutionContextDataSource executionContextDataSource) throws Exception { 20 | Object retValue = ParameterResolver.resolveArguments(executionContextDataSource) 21 | .orElseThrow(() -> new NoSuchMethodException("Cannot locate the method signature with the given input")) 22 | .invoke(executionContextDataSource::getFunctionInstance); 23 | executionContextDataSource.updateReturnValue(retValue); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodExecutors.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker; 2 | 3 | import com.microsoft.azure.functions.worker.WorkerLogManager; 4 | import org.apache.commons.lang3.SystemUtils; 5 | 6 | public class JavaMethodExecutors { 7 | public static JavaMethodExecutor createJavaMethodExecutor(ClassLoader classLoader) { 8 | if(SystemUtils.IS_JAVA_1_8) { 9 | WorkerLogManager.getSystemLogger().info("Loading JavaMethodExecutorImpl"); 10 | return JavaMethodExecutorImpl.getInstance(); 11 | } else { 12 | WorkerLogManager.getSystemLogger().info("Loading EnhancedJavaMethodExecutorImpl"); 13 | return new EnhancedJavaMethodExecutorImpl(classLoader); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/broker/JavaMethodInvokeInfo.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker; 2 | 3 | import java.lang.reflect.*; 4 | import java.util.*; 5 | 6 | import org.apache.commons.lang3.exception.*; 7 | 8 | interface InstanceSupplier { 9 | Object get() throws Exception; 10 | } 11 | 12 | /** 13 | * Used to run the actual method with specific arguments using reflection. 14 | * Thread-Safety: Single thread. 15 | */ 16 | class JavaMethodInvokeInfo { 17 | private JavaMethodInvokeInfo() {} 18 | 19 | Object invoke(InstanceSupplier instanceSupplier) throws Exception { 20 | Object instance = Modifier.isStatic(this.m.getModifiers()) ? null : instanceSupplier.get(); 21 | try { 22 | return this.m.invoke(instance, this.args); 23 | } catch (Exception ex) { 24 | return ExceptionUtils.rethrow(ex); 25 | } 26 | } 27 | 28 | private Method m; 29 | private Object[] args; 30 | 31 | static class Builder { 32 | Builder() { 33 | this.info = new JavaMethodInvokeInfo(); 34 | this.arguments = new LinkedList<>(); 35 | } 36 | 37 | JavaMethodInvokeInfo build() { 38 | assert this.info.m != null; 39 | this.info.args = this.arguments.toArray(); 40 | return this.info; 41 | } 42 | 43 | void setMethod(Method method) { this.info.m = method; } 44 | void appendArgument(Object argument) { this.arguments.add(argument); } 45 | 46 | private JavaMethodInvokeInfo info; 47 | private List arguments; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/broker/MethodBindInfo.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | public final class MethodBindInfo { 9 | 10 | private final Method method; 11 | private final List params; 12 | private final boolean hasImplicitOutput; 13 | MethodBindInfo(Method method) { 14 | this.method = method; 15 | this.params = Arrays.stream(method.getParameters()).map(ParamBindInfo::new).collect(Collectors.toList()); 16 | this.hasImplicitOutput = checkImplicitOutput(params); 17 | } 18 | 19 | private static boolean checkImplicitOutput(List params){ 20 | return params.stream().anyMatch(ParamBindInfo::isImplicitOutput); 21 | } 22 | 23 | public Method getMethod() { 24 | return method; 25 | } 26 | 27 | public List getParams() { 28 | return params; 29 | } 30 | 31 | public boolean hasEffectiveReturnType(){ 32 | boolean nonVoidReturn = this.hasNonVoidReturnType(); 33 | // For function annotated with @HasImplicitOutput, we should allow it to send back data even function's return type is void 34 | // Reference to https://github.com/microsoft/durabletask-java/issues/126 35 | boolean implicitOutput = this.hasImplicitOutput(); 36 | return nonVoidReturn || implicitOutput; 37 | } 38 | 39 | public boolean hasImplicitOutput() { 40 | return hasImplicitOutput; 41 | } 42 | 43 | public boolean hasNonVoidReturnType() { 44 | Class returnType = this.getMethod().getReturnType(); 45 | return !returnType.equals(void.class) && !returnType.equals(Void.class); 46 | } 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/broker/ParamBindInfo.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker; 2 | 3 | import java.lang.reflect.Parameter; 4 | import java.lang.reflect.Type; 5 | 6 | public final class ParamBindInfo { 7 | 8 | private final String name; 9 | private final Type type; 10 | private final String bindingNameAnnotation; 11 | private final boolean isImplicitOutput; 12 | private final Parameter parameter; 13 | public ParamBindInfo(Parameter param) { 14 | this.name = CoreTypeResolver.getAnnotationName(param); 15 | this.type = param.getParameterizedType(); 16 | this.bindingNameAnnotation = CoreTypeResolver.getBindingNameAnnotation(param); 17 | this.isImplicitOutput = CoreTypeResolver.checkImplicitOutput(param); 18 | this.parameter = param; 19 | } 20 | 21 | public boolean isImplicitOutput() { 22 | return isImplicitOutput; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public Type getType() { 30 | return type; 31 | } 32 | 33 | public String getBindingNameAnnotation() { 34 | return bindingNameAnnotation; 35 | } 36 | 37 | public Parameter getParameter() { 38 | return parameter; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/cache/WorkerObjectCache.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.cache; 2 | 3 | import com.google.common.cache.Cache; 4 | import com.google.common.cache.CacheBuilder; 5 | import com.microsoft.azure.functions.cache.CacheKey; 6 | import com.microsoft.azure.functions.cache.ObjectCache; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.function.Supplier; 10 | 11 | /** 12 | * Uses Guava's Cache, Object> with weak values 13 | * and eviction policies. 14 | */ 15 | public class WorkerObjectCache implements ObjectCache { 16 | 17 | private final Cache, Object> guavaCache; 18 | 19 | @SuppressWarnings("unchecked") 20 | public WorkerObjectCache() { 21 | // Eviction: max 1000 entries, 22 | // automatically remove if not accessed for 5 minutes, 23 | // store values as weak references. 24 | this.guavaCache = CacheBuilder.newBuilder() 25 | .maximumSize(1000) 26 | .expireAfterAccess(5, TimeUnit.MINUTES) 27 | .weakValues() 28 | .build(); 29 | } 30 | 31 | @Override 32 | public Object computeIfAbsent(Class namespace, K key, Supplier creator) { 33 | WorkerObjectCacheKey composite = new WorkerObjectCacheKey<>(namespace, key); 34 | 35 | // Guava doesn't have a direct "computeIfAbsent", but we can do get(key, Supplier). 36 | try { 37 | return guavaCache.get(composite, creator::get); 38 | } catch (Exception e) { 39 | throw new RuntimeException("Failed to computeIfAbsent for " + composite, e); 40 | } 41 | } 42 | 43 | @Override 44 | public Object get(Class namespace, K key) { 45 | return guavaCache.getIfPresent(new WorkerObjectCacheKey<>(namespace, key)); 46 | } 47 | 48 | @Override 49 | public Object remove(Class namespace, K key) { 50 | WorkerObjectCacheKey composite = new WorkerObjectCacheKey<>(namespace, key); 51 | Object oldVal = guavaCache.getIfPresent(composite); 52 | guavaCache.invalidate(composite); 53 | return oldVal; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/cache/WorkerObjectCacheKey.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.cache; 2 | 3 | import com.microsoft.azure.functions.cache.CacheKey; 4 | 5 | import java.util.Objects; 6 | 7 | /** 8 | * A composite key combining (Class namespace, K extends CacheKey). 9 | */ 10 | public class WorkerObjectCacheKey { 11 | private final Class namespace; 12 | private final K key; 13 | 14 | public WorkerObjectCacheKey(Class namespace, K key) { 15 | this.namespace = namespace; 16 | this.key = key; 17 | } 18 | 19 | public Class getNamespace() { 20 | return namespace; 21 | } 22 | 23 | public K getKey() { 24 | return key; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) return true; 30 | if (!(o instanceof WorkerObjectCacheKey)) return false; 31 | WorkerObjectCacheKey that = (WorkerObjectCacheKey) o; 32 | return Objects.equals(namespace, that.namespace) && 33 | Objects.equals(key, that.key); 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return Objects.hash(namespace, key); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "WorkerObjectCacheKey{" + 44 | "namespace=" + namespace + 45 | ", key=" + key + 46 | '}'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/chain/FunctionExecutionMiddleware.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.chain; 2 | 3 | import com.microsoft.azure.functions.internal.spi.middleware.Middleware; 4 | import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareChain; 5 | import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareContext; 6 | import com.microsoft.azure.functions.worker.binding.ExecutionContextDataSource; 7 | import com.microsoft.azure.functions.worker.broker.JavaMethodExecutor; 8 | 9 | public class FunctionExecutionMiddleware implements Middleware { 10 | 11 | private final JavaMethodExecutor functionMethodExecutor; 12 | 13 | public FunctionExecutionMiddleware(JavaMethodExecutor functionExecutionMiddleware) { 14 | this.functionMethodExecutor = functionExecutionMiddleware; 15 | } 16 | 17 | @Override 18 | public void invoke(MiddlewareContext context, MiddlewareChain chain) throws Exception{ 19 | this.functionMethodExecutor.execute((ExecutionContextDataSource) context); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/chain/InvocationChain.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.chain; 2 | 3 | 4 | import com.microsoft.azure.functions.internal.spi.middleware.Middleware; 5 | import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareChain; 6 | import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareContext; 7 | 8 | import java.util.Iterator; 9 | import java.util.List; 10 | 11 | public class InvocationChain implements MiddlewareChain { 12 | private final Iterator middlewareIterator; 13 | 14 | public InvocationChain(List middlewares){ 15 | this.middlewareIterator = middlewares.iterator(); 16 | } 17 | 18 | @Override 19 | public void doNext(MiddlewareContext context) throws Exception { 20 | if (middlewareIterator.hasNext()) { 21 | middlewareIterator.next().invoke(context, this); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/chain/InvocationChainFactory.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.chain; 2 | 3 | 4 | import com.microsoft.azure.functions.internal.spi.middleware.Middleware; 5 | import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareChain; 6 | 7 | import java.util.List; 8 | 9 | public class InvocationChainFactory { 10 | 11 | private final List middlewares; 12 | 13 | public InvocationChainFactory(List middlewares) { 14 | this.middlewares = middlewares; 15 | } 16 | 17 | public MiddlewareChain create(){ 18 | return new InvocationChain(middlewares); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/handler/FunctionLoadRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.handler; 2 | 3 | import java.util.*; 4 | import java.util.logging.Level; 5 | 6 | import com.microsoft.azure.functions.worker.Util; 7 | import com.microsoft.azure.functions.worker.WorkerLogManager; 8 | import com.microsoft.azure.functions.worker.broker.*; 9 | import com.microsoft.azure.functions.worker.description.*; 10 | import com.microsoft.azure.functions.rpc.messages.*; 11 | 12 | 13 | public class FunctionLoadRequestHandler extends MessageHandler { 14 | 15 | private final JavaFunctionBroker broker; 16 | private boolean isWarmup; 17 | 18 | public FunctionLoadRequestHandler(JavaFunctionBroker broker) { 19 | super(StreamingMessage::getFunctionLoadRequest, 20 | FunctionLoadResponse::newBuilder, 21 | FunctionLoadResponse.Builder::setResult, 22 | StreamingMessage.Builder::setFunctionLoadResponse); 23 | this.broker = broker; 24 | } 25 | 26 | public FunctionLoadRequestHandler(JavaFunctionBroker broker, boolean isWarmup){ 27 | this(broker); 28 | this.isWarmup = isWarmup; 29 | } 30 | 31 | @Override 32 | String execute(FunctionLoadRequest request, FunctionLoadResponse.Builder response) throws Exception { 33 | WorkerLogManager.getSystemLogger().log(Level.INFO, "FunctionLoadRequest received by the Java worker, Java version - " + Util.getJavaVersion()); 34 | final RpcFunctionMetadata metadata = request.getMetadata(); 35 | final FunctionMethodDescriptor descriptor = createFunctionDescriptor(request.getFunctionId(), metadata, this.isWarmup); 36 | 37 | final Map bindings = metadata.getBindingsMap(); 38 | 39 | response.setFunctionId(descriptor.getId()); 40 | this.broker.loadMethod(descriptor, bindings); 41 | 42 | return String.format("\"%s\" loaded (ID: %s, Reflection: \"%s\"::\"%s\")", 43 | descriptor.getName(), 44 | descriptor.getId(), 45 | descriptor.getJarPath(), 46 | descriptor.getFullMethodName()); 47 | } 48 | 49 | FunctionMethodDescriptor createFunctionDescriptor(String functionId, RpcFunctionMetadata metadata, boolean isWarmup) { 50 | return new FunctionMethodDescriptor(functionId, metadata.getName(), metadata.getEntryPoint(), metadata.getScriptFile(), isWarmup); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/handler/InvocationRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.handler; 2 | 3 | import java.util.*; 4 | import java.util.logging.*; 5 | 6 | import com.microsoft.azure.functions.worker.*; 7 | import com.microsoft.azure.functions.worker.broker.*; 8 | import com.microsoft.azure.functions.rpc.messages.*; 9 | 10 | public class InvocationRequestHandler extends MessageHandler { 11 | public InvocationRequestHandler(JavaFunctionBroker broker) { 12 | super(StreamingMessage::getInvocationRequest, 13 | InvocationResponse::newBuilder, 14 | InvocationResponse.Builder::setResult, 15 | StreamingMessage.Builder::setInvocationResponse); 16 | assert broker != null; 17 | this.broker = broker; 18 | this.invocationLogger = super.getLogger(); 19 | } 20 | 21 | @Override 22 | Logger getLogger() { return this.invocationLogger; } 23 | 24 | @Override 25 | String execute(InvocationRequest request, InvocationResponse.Builder response) throws Exception { 26 | WorkerLogManager.getSystemLogger().log(Level.INFO, "InvocationRequest received by the Java worker"); 27 | final String functionId = request.getFunctionId(); 28 | final String invocationId = request.getInvocationId(); 29 | 30 | this.invocationLogger = WorkerLogManager.getInvocationLogger(invocationId); 31 | response.setInvocationId(invocationId); 32 | 33 | List outputBindings = new ArrayList<>(); 34 | this.broker.invokeMethod(functionId, request, outputBindings).ifPresent(response::setReturnValue); 35 | response.addAllOutputData(outputBindings); 36 | 37 | return String.format("Function \"%s\" (Id: %s) invoked by Java Worker", 38 | this.broker.getMethodName(functionId).orElse("UNKNOWN"), invocationId); 39 | } 40 | 41 | private JavaFunctionBroker broker; 42 | private Logger invocationLogger; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/handler/RpcLogHandler.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.handler; 2 | 3 | import java.util.*; 4 | import java.util.logging.*; 5 | 6 | import org.apache.commons.lang3.exception.*; 7 | 8 | import com.microsoft.azure.functions.worker.*; 9 | import com.microsoft.azure.functions.rpc.messages.*; 10 | 11 | public class RpcLogHandler extends OutboundMessageHandler { 12 | public RpcLogHandler(LogRecord record, String invocationId) { 13 | super(() -> generateRpcLog(record, invocationId), StreamingMessage.Builder::setRpcLog); 14 | } 15 | 16 | @Override 17 | Logger getLogger() { return WorkerLogManager.getEmptyLogger(); } 18 | 19 | private static RpcLog.Builder generateRpcLog(LogRecord record, String invocationId) { 20 | RpcLog.Builder log = RpcLog.newBuilder(); 21 | /** 22 | * Check if the logging namespace belongs to system logs, invocation log should be categorized to user type (default), others should 23 | * be categorized to system type. 24 | * 25 | * local_console customer_app_insight functions_kusto_table 26 | * system_log false, false, true 27 | * user_log true, true, false 28 | */ 29 | if (invocationId == null){ 30 | log.setLogCategory(RpcLog.RpcLogCategory.System); 31 | log.setCategory(WorkerLogManager.SYSTEM_LOG_PREFIX); 32 | } 33 | Optional.ofNullable(invocationId).ifPresent(log::setInvocationId); 34 | Optional.ofNullable(record.getLoggerName()).ifPresent(log::setCategory); 35 | Optional.ofNullable(mapLogLevel(record.getLevel())).ifPresent(log::setLevel); 36 | Optional.ofNullable(record.getMessage()).ifPresent(log::setMessage); 37 | Optional.ofNullable(mapException(record.getThrown())).ifPresent(log::setException); 38 | return log; 39 | } 40 | 41 | private static RpcLog.Level mapLogLevel(Level level) { 42 | if (level.intValue() <= Level.FINE.intValue()) { 43 | return RpcLog.Level.Trace; 44 | } else if (level.intValue() < Level.INFO.intValue()) { 45 | return RpcLog.Level.Debug; 46 | } else if (level.intValue() < Level.WARNING.intValue()) { 47 | return RpcLog.Level.Information; 48 | } else if (level.intValue() < Level.SEVERE.intValue()) { 49 | return RpcLog.Level.Warning; 50 | } else { 51 | return RpcLog.Level.Error; 52 | } 53 | } 54 | 55 | private static RpcException.Builder mapException(Throwable t) { 56 | if (t == null) { return null; } 57 | RpcException.Builder exception = RpcException.newBuilder(); 58 | Optional.ofNullable(t.getMessage()).ifPresent(exception::setMessage); 59 | exception.setStackTrace(ExceptionUtils.getStackTrace(t)); 60 | return exception; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/handler/StartStreamHandler.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.handler; 2 | 3 | import java.util.logging.*; 4 | 5 | import com.microsoft.azure.functions.worker.*; 6 | import com.microsoft.azure.functions.rpc.messages.*; 7 | 8 | public class StartStreamHandler extends OutboundMessageHandler { 9 | public StartStreamHandler(String workerId) { 10 | super(() -> generateStartStream(workerId), StreamingMessage.Builder::setStartStream); 11 | } 12 | 13 | @Override 14 | Logger getLogger() { return WorkerLogManager.getSystemLogger(); } 15 | 16 | private static StartStream.Builder generateStartStream(String workerId) { 17 | assert workerId != null; 18 | StartStream.Builder startStream = StartStream.newBuilder(); 19 | startStream.setWorkerId(workerId); 20 | return startStream; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/handler/WorkerInitRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.handler; 2 | 3 | import com.microsoft.azure.functions.worker.*; 4 | import com.microsoft.azure.functions.rpc.messages.*; 5 | import com.microsoft.azure.functions.worker.broker.JavaFunctionBroker; 6 | 7 | import java.util.logging.Level; 8 | 9 | import static com.microsoft.azure.functions.worker.Constants.JAVA_APPLICATIONINSIGHTS_ENABLE_TELEMETRY; 10 | import static com.microsoft.azure.functions.worker.Constants.JAVA_ENABLE_OPENTELEMETRY; 11 | 12 | public class WorkerInitRequestHandler extends MessageHandler { 13 | public WorkerInitRequestHandler(JavaFunctionBroker broker) { 14 | super(StreamingMessage::getWorkerInitRequest, 15 | WorkerInitResponse::newBuilder, 16 | WorkerInitResponse.Builder::setResult, 17 | StreamingMessage.Builder::setWorkerInitResponse); 18 | this.broker = broker; 19 | } 20 | 21 | @Override 22 | String execute(WorkerInitRequest request, WorkerInitResponse.Builder response) { 23 | WorkerLogManager.getSystemLogger().log(Level.INFO, "WorkerInitRequest received by the Java worker"); 24 | broker.setWorkerDirectory(request.getWorkerDirectory()); 25 | response.setWorkerVersion(Application.version()); 26 | response.putCapabilities("TypedDataCollection", "TypedDataCollection"); 27 | response.putCapabilities("WorkerStatus", "WorkerStatus"); 28 | response.putCapabilities("RpcHttpBodyOnly", "RpcHttpBodyOnly"); 29 | response.putCapabilities("RpcHttpTriggerMetadataRemoved", "RpcHttpTriggerMetadataRemoved"); 30 | response.putCapabilities("HandlesWorkerTerminateMessage", "HandlesWorkerTerminateMessage"); 31 | response.putCapabilities("HandlesWorkerWarmupMessage", "HandlesWorkerWarmupMessage"); 32 | 33 | if (Boolean.parseBoolean(System.getenv(JAVA_ENABLE_OPENTELEMETRY)) || 34 | Boolean.parseBoolean(System.getenv(JAVA_APPLICATIONINSIGHTS_ENABLE_TELEMETRY))) { 35 | response.putCapabilities("WorkerOpenTelemetryEnabled", "true"); 36 | response.putCapabilities("WorkerApplicationInsightsLoggingEnabled", "true"); 37 | } 38 | 39 | response.setWorkerMetadata(composeWorkerMetadata()); 40 | 41 | return "Worker initialized"; 42 | } 43 | 44 | private WorkerMetadata.Builder composeWorkerMetadata(){ 45 | WorkerMetadata.Builder workerMetadataBuilder = WorkerMetadata.newBuilder(); 46 | workerMetadataBuilder.setRuntimeName("java"); 47 | workerMetadataBuilder.setRuntimeVersion(System.getProperty("java.version")); 48 | workerMetadataBuilder.setWorkerVersion(Application.version()); 49 | workerMetadataBuilder.setWorkerBitness(System.getProperty("os.arch")); 50 | return workerMetadataBuilder; 51 | } 52 | 53 | private final JavaFunctionBroker broker; 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/handler/WorkerStatusRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.handler; 2 | import com.microsoft.azure.functions.rpc.messages.*; 3 | 4 | public class WorkerStatusRequestHandler extends MessageHandler { 5 | 6 | public WorkerStatusRequestHandler() { 7 | super(StreamingMessage::getWorkerStatusRequest, 8 | WorkerStatusResponse::newBuilder, 9 | null, 10 | StreamingMessage.Builder::setWorkerStatusResponse); 11 | } 12 | 13 | @Override 14 | String execute(WorkerStatusRequest request, WorkerStatusResponse.Builder response) { 15 | return String.format("WorkerStatusRequest completed"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/handler/WorkerTerminateRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.handler; 2 | 3 | import com.google.protobuf.Message; 4 | import com.microsoft.azure.functions.rpc.messages.StreamingMessage; 5 | import com.microsoft.azure.functions.rpc.messages.WorkerTerminate; 6 | import com.microsoft.azure.functions.worker.JavaWorkerClient; 7 | import com.microsoft.azure.functions.worker.WorkerLogManager; 8 | import org.apache.commons.lang3.exception.ExceptionUtils; 9 | 10 | import java.util.logging.Level; 11 | 12 | public class WorkerTerminateRequestHandler extends MessageHandler { 13 | 14 | public WorkerTerminateRequestHandler() { 15 | super(StreamingMessage::getWorkerTerminate, 16 | () -> null, 17 | null, 18 | null); 19 | } 20 | 21 | @Override 22 | String execute(WorkerTerminate workerTerminate, Message.Builder builder) { 23 | WorkerLogManager.getSystemLogger().log(Level.INFO, "Worker terminate request received. Gracefully shutting down the worker."); 24 | // Flushing the logs. Keeping the grpc client connection open here in case it is used by user's shutdown hooks 25 | WorkerLogManager.flushLogs(); 26 | System.exit(0); 27 | return null; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/reflect/ClassLoaderProvider.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.reflect; 2 | 3 | import java.io.*; 4 | import java.net.*; 5 | 6 | public interface ClassLoaderProvider { 7 | 8 | /* 9 | * Adds a customer search path to be used by the provided class loader 10 | */ 11 | void addCustomerUrl(URL url) throws IOException; 12 | 13 | /* 14 | * Adds a worker search path to be used by the provided class loader 15 | */ 16 | void addWorkerUrl(URL url) throws IOException; 17 | 18 | /* 19 | * Create the class loader with the required search paths 20 | */ 21 | ClassLoader createClassLoader(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/reflect/DefaultClassLoaderProvider.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.reflect; 2 | 3 | import java.io.*; 4 | import java.lang.reflect.*; 5 | import java.net.*; 6 | import java.util.*; 7 | import java.util.concurrent.*; 8 | 9 | import com.microsoft.azure.functions.worker.*; 10 | 11 | /** 12 | * @author Kevin Hillinger Default implementation of the class loader provider 13 | */ 14 | public class DefaultClassLoaderProvider implements ClassLoaderProvider { 15 | public DefaultClassLoaderProvider() { 16 | customerUrls = Collections.newSetFromMap(new ConcurrentHashMap()); 17 | workerUrls = Collections.newSetFromMap(new ConcurrentHashMap()); 18 | } 19 | 20 | /* 21 | * @see com.microsoft.azure.functions.reflect.ClassLoaderProvider#createClassLoader() 22 | */ 23 | @Override 24 | public ClassLoader createClassLoader() { 25 | List urlsList = new ArrayList<>(); 26 | urlsList.addAll(customerUrls); 27 | urlsList.addAll(workerUrls); 28 | URL[] urlsForClassLoader = urlsList.toArray(new URL[0]); 29 | URLClassLoader classLoader = new URLClassLoader(urlsForClassLoader); 30 | Thread.currentThread().setContextClassLoader(classLoader); 31 | return classLoader; 32 | } 33 | 34 | @Override 35 | public void addCustomerUrl(URL url) throws IOException { 36 | if (customerUrls.contains(url)) { 37 | return; 38 | } 39 | WorkerLogManager.getSystemLogger().info("Loading customer file URL: " + url); 40 | customerUrls.add(url); 41 | addUrlToSystemClassLoader(url); 42 | } 43 | 44 | @Override 45 | public void addWorkerUrl(URL url) throws IOException { 46 | if (workerUrls.contains(url)) { 47 | return; 48 | } 49 | WorkerLogManager.getSystemLogger().info("Loading worker file URL: " + url); 50 | workerUrls.add(url); 51 | addUrlToSystemClassLoader(url); 52 | } 53 | 54 | private void addUrlToSystemClassLoader(URL url) throws IOException { 55 | URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader(); 56 | Class sysclass = URLClassLoader.class; 57 | 58 | try { 59 | Method method = sysclass.getDeclaredMethod(SYS_LOADER_ADDURL_METHOD_NAME, parameters); 60 | method.setAccessible(true); 61 | method.invoke(sysloader, new Object[] { url }); 62 | } catch (Throwable t) { 63 | throw new IOException("Error adding " + url + " to system classloader"); 64 | } 65 | 66 | } 67 | 68 | private static final String SYS_LOADER_ADDURL_METHOD_NAME = "addURL"; 69 | private static final Class[] parameters = new Class[] { URL.class }; 70 | private final Set customerUrls; 71 | private final Set workerUrls; 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/reflect/EnhancedClassLoaderProvider.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.reflect; 2 | 3 | import java.io.*; 4 | import java.net.*; 5 | import java.util.*; 6 | import java.util.concurrent.*; 7 | 8 | 9 | import com.microsoft.azure.functions.worker.*; 10 | 11 | public class EnhancedClassLoaderProvider implements ClassLoaderProvider { 12 | 13 | public EnhancedClassLoaderProvider() { 14 | customerUrls = Collections.newSetFromMap(new ConcurrentHashMap()); 15 | workerUrls = Collections.newSetFromMap(new ConcurrentHashMap()); 16 | } 17 | 18 | /* 19 | * @see com.microsoft.azure.functions.reflect.ClassLoaderProvider#createClassLoader() 20 | */ 21 | @Override 22 | public ClassLoader createClassLoader() { 23 | if (classLoaderInstance == null) { 24 | synchronized (lock) { 25 | if (classLoaderInstance == null) { 26 | List urlsList = new ArrayList<>(); 27 | urlsList.addAll(customerUrls); 28 | urlsList.addAll(workerUrls); 29 | URL[] urlsForClassLoader = urlsList.toArray(new URL[0]); 30 | URLClassLoader loader = new URLClassLoader(urlsForClassLoader); 31 | classLoaderInstance = loader; 32 | } 33 | } 34 | } 35 | return classLoaderInstance; 36 | } 37 | 38 | @Override 39 | public void addCustomerUrl(URL url) throws IOException { 40 | if (customerUrls.contains(url)) { 41 | return; 42 | } 43 | WorkerLogManager.getSystemLogger().info("Loading customer file URL: " + url); 44 | customerUrls.add(url); 45 | } 46 | 47 | @Override 48 | public void addWorkerUrl(URL url) throws IOException { 49 | if (workerUrls.contains(url)) { 50 | return; 51 | } 52 | WorkerLogManager.getSystemLogger().info("Loading worker file URL: " + url); 53 | workerUrls.add(url); 54 | } 55 | 56 | private final Set customerUrls; 57 | private final Set workerUrls; 58 | private final Object lock = new Object(); 59 | private volatile URLClassLoader classLoaderInstance; 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/azure/functions/worker/reflect/FactoryClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.reflect; 2 | 3 | import org.apache.commons.lang3.SystemUtils; 4 | 5 | public class FactoryClassLoader { 6 | public ClassLoaderProvider createClassLoaderProvider(){ 7 | if(SystemUtils.IS_JAVA_1_8) { 8 | return new DefaultClassLoaderProvider(); 9 | } else { 10 | return new EnhancedClassLoaderProvider(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/UtilTest.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker; 2 | 3 | 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertFalse; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | public class UtilTest { 10 | 11 | @Test 12 | public void isTrueNull() { 13 | assertFalse(Util.isTrue(null)); 14 | } 15 | 16 | @Test 17 | public void isTrueEmptyString() { 18 | assertFalse(Util.isTrue("")); 19 | } 20 | 21 | @Test 22 | public void isTrueValueTrue() { 23 | assertTrue(Util.isTrue("True")); 24 | } 25 | } -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcIntegerDataSourceTest.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.binding.tests; 2 | 3 | 4 | import java.lang.invoke.WrongMethodTypeException; 5 | import java.util.Optional; 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | 8 | 9 | import com.microsoft.azure.functions.worker.binding.BindingData; 10 | import com.microsoft.azure.functions.worker.binding.RpcIntegerDataSource; 11 | import org.junit.jupiter.api.Test; 12 | 13 | public class RpcIntegerDataSourceTest { 14 | 15 | @Test 16 | public void rpcIntegerDataSource_To_Integer() { 17 | String sourceKey = "testInt"; 18 | Integer input = 1234; 19 | RpcIntegerDataSource stringData = new RpcIntegerDataSource(sourceKey, input); 20 | BindingData bindingData = new BindingData(input); 21 | 22 | Optional actualBindingData = stringData.computeByName(sourceKey, Integer.class); 23 | BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); 24 | assertEquals(bindingData.getValue(), actualArg.getValue()); 25 | 26 | actualBindingData = stringData.computeByName(sourceKey, Short.class); 27 | actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); 28 | bindingData = new BindingData(input.shortValue()); 29 | assertEquals((Short) bindingData.getValue(), actualArg.getValue()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/broker/tests/FunctionDefinitionTests.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/src/test/java/com/microsoft/azure/functions/worker/broker/tests/FunctionDefinitionTests.java -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/broker/tests/InvalidCustomBinding.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker.tests; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.ElementType; 7 | 8 | @Target(ElementType.PARAMETER) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface InvalidCustomBinding { 11 | String index(); 12 | String path(); 13 | } -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/broker/tests/TestCustomBinding.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker.tests; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.ElementType; 7 | 8 | import com.microsoft.azure.functions.annotation.CustomBinding; 9 | 10 | @Target(ElementType.PARAMETER) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @CustomBinding(direction = "in", name = "", type = "customBinding") 13 | public @interface TestCustomBinding { 14 | String index(); 15 | String path(); 16 | String name(); 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/broker/tests/TestCustomBindingNoName.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker.tests; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.ElementType; 7 | 8 | import com.microsoft.azure.functions.annotation.CustomBinding; 9 | 10 | @Target(ElementType.PARAMETER) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @CustomBinding(direction = "in", name = "cutomBindingName", type = "customBinding") 13 | public @interface TestCustomBindingNoName { 14 | String index(); 15 | String path(); 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/broker/tests/TestFunctionsClass.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/src/test/java/com/microsoft/azure/functions/worker/broker/tests/TestFunctionsClass.jar -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/broker/tests/TestFunctionsClass.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.broker.tests; 2 | 3 | import java.lang.*; 4 | import java.util.*; 5 | import com.microsoft.azure.functions.annotation.*; 6 | import com.microsoft.azure.functions.*; 7 | 8 | /** 9 | * Azure Functions with HTTP Trigger. 10 | */ 11 | public class TestFunctionsClass 12 | { 13 | 14 | public HttpResponseMessage TestHttpTrigger( 15 | @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 16 | final ExecutionContext context 17 | ) { 18 | return request.createResponseBuilder(HttpStatus.OK).build(); 19 | } 20 | 21 | public HttpResponseMessage TestHttpTriggerWithOutAnnotation( 22 | @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 23 | final ExecutionContext context 24 | ) { 25 | return request.createResponseBuilder(HttpStatus.OK).build(); 26 | } 27 | 28 | public String TestHttpTriggerOverload( 29 | @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) String inputRequest, 30 | final ExecutionContext context 31 | ) { 32 | return "Hello"; 33 | } 34 | 35 | public HttpResponseMessage TestHttpTriggerOverload( 36 | @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, 37 | final ExecutionContext context 38 | ) { 39 | return request.createResponseBuilder(HttpStatus.OK).build(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/functional/tests/SimpleParamReturnTest.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.functional.tests; 2 | 3 | import com.microsoft.azure.functions.rpc.messages.*; 4 | import com.microsoft.azure.functions.worker.test.utilities.*; 5 | import org.junit.jupiter.params.ParameterizedTest; 6 | import org.junit.jupiter.params.provider.ValueSource; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | public class SimpleParamReturnTest extends FunctionsTestBase { 11 | private static String stringReturnValue; 12 | 13 | public String ReturnStringFunction() { 14 | return stringReturnValue; 15 | } 16 | 17 | @ParameterizedTest 18 | @ValueSource(strings = {"test String"}) 19 | public void testStringData(String stringInput) throws Exception { 20 | stringReturnValue = stringInput; 21 | System.setProperty("azure.functions.worker.java.skip.testing", "true"); 22 | try (FunctionsTestHost host = new FunctionsTestHost()) { 23 | this.loadFunction(host, "returnStringTestId", "ReturnStringFunction"); 24 | InvocationResponse stringResponse = host.call("getret", "returnStringTestId"); 25 | assertEquals(TypedData.DataCase.STRING, stringResponse.getReturnValue().getDataCase()); 26 | assertEquals(stringInput, stringResponse.getReturnValue().getString()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/handler/tests/FunctionEnvironmentReloadRequestHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.handler.tests; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import com.microsoft.azure.functions.worker.broker.JavaFunctionBroker; 9 | import com.microsoft.azure.functions.worker.handler.FunctionEnvironmentReloadRequestHandler; 10 | import com.microsoft.azure.functions.worker.reflect.DefaultClassLoaderProvider; 11 | import org.junit.jupiter.api.Test; 12 | 13 | public class FunctionEnvironmentReloadRequestHandlerTest { 14 | 15 | @Test 16 | public void SetEnv_Succeeds() throws Exception { 17 | DefaultClassLoaderProvider classLoader = new DefaultClassLoaderProvider(); 18 | JavaFunctionBroker broker = new JavaFunctionBroker(classLoader); 19 | FunctionEnvironmentReloadRequestHandler envHandler = new FunctionEnvironmentReloadRequestHandler(broker); 20 | 21 | String testSetting = System.getenv("testSetting"); 22 | assertNull(testSetting); 23 | Map existingVariables = System.getenv(); 24 | Map newEnvVariables = new HashMap<>(); 25 | newEnvVariables.putAll(existingVariables); 26 | newEnvVariables.put("testSetting", "testSettingValue"); 27 | envHandler.setEnv(newEnvVariables); 28 | testSetting = System.getenv("testSetting"); 29 | assertNotNull(testSetting); 30 | assertEquals( "testSettingValue", testSetting); 31 | } 32 | 33 | @Test 34 | public void SetEnv_Null_Succeeds() throws Exception { 35 | DefaultClassLoaderProvider classLoader = new DefaultClassLoaderProvider(); 36 | JavaFunctionBroker broker = new JavaFunctionBroker(classLoader); 37 | FunctionEnvironmentReloadRequestHandler envHandler = new FunctionEnvironmentReloadRequestHandler(broker); 38 | 39 | Map newEnvVariables = null; 40 | envHandler.setEnv(newEnvVariables); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/test/ExecutionTraceContextTest.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.test; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import java.util.HashMap; 6 | import com.microsoft.azure.functions.TraceContext; 7 | import com.microsoft.azure.functions.worker.binding.ExecutionTraceContext; 8 | import org.junit.jupiter.api.Test; 9 | 10 | 11 | public class ExecutionTraceContextTest { 12 | 13 | @Test 14 | public void TraceContext_test_getAndset_nonEmpty() { 15 | String traceParent = "randomTraceParent"; 16 | String traceState = "randomTraceState"; 17 | HashMap attributes = new HashMap(); 18 | 19 | attributes.put("key1", "value1"); 20 | attributes.put("key2", "value2"); 21 | 22 | TraceContext testTraceContext = new ExecutionTraceContext(traceParent, traceState, attributes); 23 | assertEquals(traceParent, testTraceContext.getTraceparent()); 24 | assertEquals(traceState, testTraceContext.getTracestate()); 25 | assertEquals(traceState, testTraceContext.getTracestate()); 26 | assertEquals(attributes, testTraceContext.getAttributes()); 27 | } 28 | 29 | @Test 30 | public void TraceContext_test_getAndset_Empty() { 31 | String traceParent = ""; 32 | String traceState = ""; 33 | HashMap attributes = new HashMap(); 34 | TraceContext testTraceContext = new ExecutionTraceContext(traceParent, traceState, attributes); 35 | assertEquals(traceParent, testTraceContext.getTraceparent()); 36 | assertEquals(traceState, testTraceContext.getTracestate()); 37 | assertEquals(traceState, testTraceContext.getTracestate()); 38 | assertEquals(attributes, testTraceContext.getAttributes()); 39 | } 40 | 41 | @Test 42 | public void TraceContext_test_getAndset_Null() { 43 | TraceContext testTraceContext = new ExecutionTraceContext(null, null, null); 44 | assertEquals(null, testTraceContext.getTraceparent()); 45 | assertEquals(null, testTraceContext.getTracestate()); 46 | assertEquals(null, testTraceContext.getTracestate()); 47 | assertEquals(null, testTraceContext.getAttributes()); 48 | } 49 | } -------------------------------------------------------------------------------- /src/test/java/com/microsoft/azure/functions/worker/test/utilities/FunctionsTestBase.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.worker.test.utilities; 2 | 3 | import java.lang.reflect.*; 4 | import java.util.*; 5 | 6 | import com.microsoft.azure.functions.rpc.messages.*; 7 | 8 | public class FunctionsTestBase { 9 | protected void loadFunction(FunctionsTestHost host, String id, String method) throws Exception { 10 | String fullname = this.getClass().getCanonicalName() + "." + method; 11 | Map bindings = new HashMap<>(); 12 | Method entry = this.getClass().getMethod(method); 13 | if (!entry.getReturnType().equals(Void.TYPE)) { 14 | bindings.put("$return", BindingInfo.newBuilder().setDirection(BindingInfo.Direction.out).build()); 15 | } 16 | host.loadFunction(id, fullname, bindings); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /tools/.images/remote-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/tools/.images/remote-config.png -------------------------------------------------------------------------------- /tools/.images/worker-debug-configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-functions-java-worker/b0b564020b6e3a4449011e2c3eb59606554adc12/tools/.images/worker-debug-configuration.png -------------------------------------------------------------------------------- /tools/AzureFunctionsJavaWorker.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.Azure.Functions.JavaWorker 5 | Microsoft Azure Functions Java Worker 6 | java azure-functions azure 7 | $version$ 8 | Microsoft 9 | Microsoft 10 | https://github.com/Azure/azure-functions-java-worker 11 | https://github.com/Azure/azure-functions-java-worker/blob/master/LICENSE 12 | https://raw.githubusercontent.com/Azure/azure-functions-cli/master/src/Azure.Functions.Cli/npm/assets/azure-functions-logo-color-raster.png 13 | false 14 | Microsoft Azure Functions Java Worker 15 | Copyright (c) Microsoft Corporation. All rights reserved. 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Build and publishing 2 | 3 | In order for the Java Worker to get loaded into the runtime, today we use a NuGet to package the jars for deployment. 4 | 5 | ## Azure Pipelines 6 | 7 | We're using Azure Pipelines for CI. It builds the Java Core and Worker modules and then packages them into a NuGet for consumption by the Azure Functions Script Host. 8 | 9 | CI runs on every PR and nightly on 'dev' and 'v3.x' branches. 10 | 11 | ## Local 12 | 13 | To produce the build results locally, be sure you have Java, Maven, and NuGet installed. 14 | 15 | ```powershell 16 | mvn clean install -DskipTests 17 | mkdir pkg 18 | Get-ChildItem -Path .\azure-functions-java-worker\target\* -Include 'azure*' -Exclude '*shaded.jar' | %{ Copy-Item $_.FullName .\pkg\azure-functions-java-worker.jar } 19 | nuget pack ./tools/AzureFunctionsJavaWorker.nuspec -Properties versionsuffix=LOCAL 20 | ``` 21 | 22 | This result in a `.nupkg` file which you can use for adhoc testing. -------------------------------------------------------------------------------- /warmup-function/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.azfs.java 6 | warmup-httptrigger 7 | 1.0.0 8 | jar 9 | 10 | Azure Java Functions 11 | 12 | 13 | UTF-8 14 | 1.8 15 | 16 | 17 | 18 | 19 | com.microsoft.azure.functions 20 | azure-functions-java-core-library 21 | 1.2.0 22 | provided 23 | 24 | 25 | 26 | 27 | warmup-httptrigger 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-compiler-plugin 32 | 3.8.1 33 | 34 | ${java.version} 35 | ${java.version} 36 | ${project.build.sourceEncoding} 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /warmup-function/src/main/java/com/microsoft/azure/functions/warmup/java/Function.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.azure.functions.warmup.java; 2 | 3 | import com.microsoft.azure.functions.*; 4 | import java.util.HashSet; 5 | import java.util.Optional; 6 | import java.util.Set; 7 | import java.util.UUID; 8 | 9 | 10 | /** 11 | * Azure Functions with HTTP Trigger. 12 | */ 13 | public class Function { 14 | 15 | public HttpResponseMessage run( 16 | @HttpTrigger(name = "req") HttpRequestMessage> request, final ExecutionContext context) { 17 | Set set = new HashSet<>(); 18 | set.add(UUID.randomUUID()); 19 | set.add(UUID.randomUUID()); 20 | for (UUID item : set) { 21 | int id = item.hashCode(); 22 | } 23 | return request.createResponseBuilder(HttpStatus.OK).body("cx warm up completed").build(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /warmup-function/src/main/java/com/microsoft/azure/functions/warmup/java/HttpTrigger.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for 4 | * license information. 5 | */ 6 | 7 | package com.microsoft.azure.functions.warmup.java; 8 | 9 | import java.lang.annotation.ElementType; 10 | import java.lang.annotation.Retention; 11 | import java.lang.annotation.RetentionPolicy; 12 | import java.lang.annotation.Target; 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.PARAMETER) 15 | public @interface HttpTrigger { 16 | String name(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /worker.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": { 3 | "language": "java", 4 | "extensions": [".jar"], 5 | "defaultExecutablePath": "%JAVA_HOME%/bin/java", 6 | "defaultWorkerPath": "azure-functions-java-worker.jar", 7 | "arguments": ["-XX:+TieredCompilation -XX:TieredStopAtLevel=1 -noverify -Djava.net.preferIPv4Stack=true -jar", "%JAVA_OPTS%", "%AZURE_FUNCTIONS_MESH_JAVA_OPTS%"] 8 | }, 9 | "profiles": 10 | [ 11 | { 12 | "profileName":"AppInsightsOptOutWindows", 13 | "conditions":[ 14 | {"conditionType":"environment","conditionName":"languageWorkers:java:arguments","conditionExpression":".*-DaiAgentOptOut.*"}, 15 | {"conditionType":"hostProperty","conditionName":"platform","conditionExpression":"WINDOWS"} 16 | ], 17 | "description":{ 18 | "arguments": ["-XX:+TieredCompilation -XX:TieredStopAtLevel=1 -noverify -Djava.net.preferIPv4Stack=true -jar", "%JAVA_OPTS%", "%AZURE_FUNCTIONS_MESH_JAVA_OPTS%"] 19 | } 20 | }, 21 | { 22 | "profileName":"AppInsightsOptOutLinux", 23 | "conditions":[ 24 | {"conditionType":"environment","conditionName":"languageWorkers__java__arguments","conditionExpression":".*-DaiAgentOptOut.*"}, 25 | {"conditionType":"hostProperty","conditionName":"platform","conditionExpression":"LINUX"} 26 | ], 27 | "description":{ 28 | "arguments": ["-XX:+TieredCompilation -XX:TieredStopAtLevel=1 -noverify -Djava.net.preferIPv4Stack=true -jar", "%JAVA_OPTS%", "%AZURE_FUNCTIONS_MESH_JAVA_OPTS%"] 29 | } 30 | }, 31 | { 32 | "profileName":"AppInsightsPlaceholder", 33 | "conditions":[ 34 | {"conditionType":"environment","conditionName":"INITIALIZED_FROM_PLACEHOLDER","conditionExpression":"(?i)true$"} 35 | ], 36 | "description":{ 37 | "arguments":["-XX:+TieredCompilation -XX:TieredStopAtLevel=1 -noverify -Djava.net.preferIPv4Stack=true -javaagent:\"{workerDirectoryPath}/agent/applicationinsights-agent.jar\" -DLazySetOptIn=false -jar", "%JAVA_OPTS%", "%AZURE_FUNCTIONS_MESH_JAVA_OPTS%"] 38 | } 39 | }, 40 | { 41 | "profileName":"AppInsightsNoPlaceholder", 42 | "conditions":[ 43 | {"conditionType":"environment","conditionName":"APPLICATIONINSIGHTS_ENABLE_AGENT","conditionExpression":"(?i)true$"} 44 | ], 45 | "description":{ 46 | "arguments": ["-XX:+TieredCompilation -XX:TieredStopAtLevel=1 -noverify -Djava.net.preferIPv4Stack=true -javaagent:\"{workerDirectoryPath}/agent/applicationinsights-agent.jar\" -jar", "%JAVA_OPTS%", "%AZURE_FUNCTIONS_MESH_JAVA_OPTS%"] 47 | } 48 | } 49 | ] 50 | } --------------------------------------------------------------------------------