Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ steps:
checkLatest: true
- pwsh: |
Get-Command mvn
displayName: 'Installing Maven'
displayName: 'Check Maven is installed'
- pwsh: |
$buildNumber = 0
if($env:APPVEYOR_REPO_TAG -eq "true") {
Expand All @@ -32,10 +32,10 @@ steps:
CleanTargetFolder: true
displayName: 'Copying files for artifacts'
- pwsh: |
.\setup-tests.ps1
.\setup-tests-pipeline.ps1
displayName: 'Setting tests'
- pwsh: |
.\run-tests.ps1
.\build-run-tests-pipeline.ps1
env:
AzureWebJobsStorage: $(AzureWebJobsStorage)
AzureWebJobsCosmosDBConnectionString: $(AzureWebJobsCosmosDBConnectionString)
Expand All @@ -47,8 +47,25 @@ steps:
SBTopicSubName: $(SBTopicSubName)
CosmosDBDatabaseName: $(CosmosDBDatabaseName)
SBQueueName: $(SBQueueName)
displayName: 'running tests'
continueOnError: true
displayName: 'Build & Run tests for java 8'
continueOnError: false
- pwsh: |
.\build-run-tests-pipeline.ps1
env:
JAVA_HOME: 'C:\Program Files\Java\zulu-11-azure-jdk_11.33.15-11.0.4-win_x64'
AzureWebJobsStorage: $(AzureWebJobsStorage)
AzureWebJobsCosmosDBConnectionString: $(AzureWebJobsCosmosDBConnectionString)
AzureWebJobsServiceBus: $(AzureWebJobsServiceBus)
AzureWebJobsEventHubSender_2: $(AzureWebJobsEventHubSender_2)
AzureWebJobsEventHubReceiver: $(AzureWebJobsEventHubReceiver)
AzureWebJobsEventHubSender: $(AzureWebJobsEventHubSender)
AzureWebJobsEventHubPath: $(AzureWebJobsEventHubPath)
SBTopicName: $(SBTopicName)
SBTopicSubName: $(SBTopicSubName)
CosmosDBDatabaseName: $(CosmosDBDatabaseName)
SBQueueName: $(SBQueueName)
displayName: 'Build & Run tests for java 11'
continueOnError: false
- task: CopyFiles@2
inputs:
SourceFolder: '$(System.DefaultWorkingDirectory)/testResults'
Expand Down
53 changes: 53 additions & 0 deletions build-run-tests-pipeline.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# A function that checks exit codes and fails script if an error is found
function StopOnFailedExecution {
if ($LastExitCode)
{
exit $LastExitCode
}
}

function RunTest([string] $project, [string] $description,[bool] $skipBuild = $false, $filter = $null) {
Write-Host "Running test: $description" -ForegroundColor DarkCyan
Write-Host "-----------------------------------------------------------------------------" -ForegroundColor DarkCyan
Write-Host

$cmdargs = "test", "$project", "-v", "q", "-l", "trx", "-r",".\testResults"

if ($filter) {
$cmdargs += "--filter", "$filter"
}

& dotnet $cmdargs | Out-Host
$r = $?

Write-Host
Write-Host "-----------------------------------------------------------------------------" -ForegroundColor DarkCyan
Write-Host

return $r
}

$currDir = Get-Location

Write-Host "Building endtoendtests...."
$Env:Path = $Env:Path+";$currDir\Azure.Functions.Cli"
Push-Location -Path "./endtoendtests" -StackName javaWorkerDir
Write-Host "Building azure-functions-maven-com.microsoft.azure.functions.endtoendtests"
cmd.exe /c '.\..\mvnBuildSkipTests.bat'
StopOnFailedExecution
Pop-Location -StackName "javaWorkerDir"

$tests = @(
@{project ="endtoendtests\Azure.Functions.Java.Tests.E2E\Azure.Functions.Java.Tests.E2E\Azure.Functions.Java.Tests.E2E.csproj"; description="E2E integration tests"}
)

$success = $true
$testRunSucceeded = $true

foreach ($test in $tests){
$testRunSucceeded = RunTest $test.project $test.description $testRunSucceeded $test.filter
$success = $testRunSucceeded -and $success
}

if (-not $success) { exit 1 }

28 changes: 28 additions & 0 deletions endtoendtests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,34 @@
<groupId>com.microsoft.azure.functions</groupId>
<artifactId>azure-functions-java-library</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.9</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@
import com.microsoft.azure.functions.*;
import java.util.*;

import com.google.gson.Gson;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

import java.io.InputStream;
import com.google.common.io.CharStreams;
import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.commons.lang3.SystemUtils;

/**
* Azure Functions with HTTP trigger.
*/
Expand All @@ -15,14 +32,28 @@ public class HttpTriggerTests {
public HttpResponseMessage HttpTriggerJava(
@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context
) {
) throws Exception {
context.getLogger().info("Java HTTP trigger processed a request.");

// Parse query parameters
String query = request.getQueryParameters().get("name");
String name = request.getBody().orElse(query);
String readEnv = System.getenv("AzureWebJobsStorage");

try (Connection connection = DriverManager.getConnection("jdbc:h2:mem:test")) {
try (Statement statement = connection.createStatement()) {
statement.execute("select 1");
}
}

Gson a = new Gson();

// if(!SystemUtils.IS_JAVA_15) {
// context.getLogger().info("Java version not 15");
// }

get("https://httpstat.us/200");

if (name == null ) {
return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build();
}
Expand All @@ -32,6 +63,18 @@ public HttpResponseMessage HttpTriggerJava(
return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
}

private static String get(String url) throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Accept", "application/json");
HttpResponse response = httpClient.execute(httpGet);
InputStream content = response.getEntity().getContent();
String body = CharStreams.toString(new InputStreamReader(content));
content.close();
httpClient.close();
return "Response from " + url + " was: " + body;
}

@FunctionName("HttpTriggerJavaThrows")
public HttpResponseMessage HttpTriggerThrows(
@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<azure.functions.java.library.version>1.3.1-SNAPSHOT</azure.functions.java.library.version>
<azure.functions.java.library.version>1.3.1</azure.functions.java.library.version>
</properties>

<licenses>
Expand Down
39 changes: 39 additions & 0 deletions setup-tests-pipeline.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Write-Host "$args[0]"
Write-Host $args[0]

$skipCliDownload = $false
if($args[0])
{
$skipCliDownload = $args[0]
}
Write-Host $skipCliDownload

$currDir = Get-Location
if(!$skipCliDownload)
{
Write-Host "Deleting Functions Core Tools if exists...."
Remove-Item -Force ./Azure.Functions.Cli.zip -ErrorAction Ignore
Remove-Item -Recurse -Force ./Azure.Functions.Cli -ErrorAction Ignore

Write-Host "Downloading Functions Core Tools...."
Invoke-RestMethod -Uri 'https://functionsclibuilds.blob.core.windows.net/builds/3/latest/version.txt' -OutFile version.txt
Write-Host "Using Functions Core Tools version: $(Get-Content -Raw version.txt)"
$version = "$(Get-Content -Raw version.txt)"
Remove-Item version.txt

if ($version -and $version.trim())
{
$env:CORE_TOOLS_URL = "https://functionsclibuilds.blob.core.windows.net/builds/3/latest/Azure.Functions.Cli.win-x86.zip"
}
Write-Host "CORE_TOOLS_URL: $env:CORE_TOOLS_URL"
$output = "$currDir\Azure.Functions.Cli.zip"
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($env:CORE_TOOLS_URL, $output)

Write-Host "Extracting Functions Core Tools...."
Expand-Archive ".\Azure.Functions.Cli.zip" -DestinationPath ".\Azure.Functions.Cli"
}
Write-Host "Copying azure-functions-java-worker to Functions Host workers directory...."
Get-ChildItem -Path .\target\* -Include 'azure*' -Exclude '*shaded.jar','*tests.jar' | %{ Copy-Item $_.FullName ".\Azure.Functions.Cli\workers\java\azure-functions-java-worker.jar" }
Copy-Item ".\worker.config.json" ".\Azure.Functions.Cli\workers\java"

19 changes: 10 additions & 9 deletions setup-tests.ps1
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@

# A function that checks exit codes and fails script if an error is found
function StopOnFailedExecution {
if ($LastExitCode)
{
exit $LastExitCode
if ($LastExitCode)
{
exit $LastExitCode
}
}
Write-Host "$args[0]"
Expand All @@ -24,13 +24,14 @@ if(!$skipCliDownload)
Remove-Item -Recurse -Force ./Azure.Functions.Cli -ErrorAction Ignore

Write-Host "Downloading Functions Core Tools...."
Invoke-RestMethod -Uri 'https://functionsclibuilds.blob.core.windows.net/builds/2/latest/version.txt' -OutFile version.txt
Invoke-RestMethod -Uri 'https://functionsclibuilds.blob.core.windows.net/builds/3/latest/version.txt' -OutFile version.txt
Write-Host "Using Functions Core Tools version: $(Get-Content -Raw version.txt)"
$version = "$(Get-Content -Raw version.txt)"
Remove-Item version.txt

if (-not (Test-Path env:CORE_TOOLS_URL))
{
$env:CORE_TOOLS_URL = "https://functionsclibuilds.blob.core.windows.net/builds/2/latest/Azure.Functions.Cli.win-x86.zip"
if ($version -and $version.trim())
{
$env:CORE_TOOLS_URL = "https://functionsclibuilds.blob.core.windows.net/builds/3/latest/Azure.Functions.Cli.win-x86.zip"
}
Write-Host "CORE_TOOLS_URL: $env:CORE_TOOLS_URL"
$output = "$currDir\Azure.Functions.Cli.zip"
Expand All @@ -41,13 +42,13 @@ if(!$skipCliDownload)
Expand-Archive ".\Azure.Functions.Cli.zip" -DestinationPath ".\Azure.Functions.Cli"
}
Write-Host "Copying azure-functions-java-worker to Functions Host workers directory...."
Get-ChildItem -Path .\target\* -Include 'azure*' -Exclude '*shaded.jar' | %{ Copy-Item $_.FullName ".\Azure.Functions.Cli\workers\java\azure-functions-java-worker.jar" }
Get-ChildItem -Path .\target\* -Include 'azure*' -Exclude '*shaded.jar','*tests.jar' | %{ Copy-Item $_.FullName ".\Azure.Functions.Cli\workers\java\azure-functions-java-worker.jar" }
Copy-Item ".\worker.config.json" ".\Azure.Functions.Cli\workers\java"

Write-Host "Building endtoendtests...."
$Env:Path = $Env:Path+";$currDir\Azure.Functions.Cli"
Push-Location -Path "./endtoendtests" -StackName javaWorkerDir
Write-Host "Building azure-functions-maven-com.microsoft.azure.functions.endtoendtests"
Write-Host "Building azure-functions-maven-com.microsoft.azure.functions.endtoendtests"
cmd.exe /c '.\..\mvnBuildSkipTests.bat'
StopOnFailedExecution
Pop-Location -StackName "javaWorkerDir"
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public JavaWorkerClient(IApplication app) {
this.channel = chanBuilder.build();
this.peer = new AtomicReference<>(null);
this.handlerSuppliers = new HashMap<>();
this.classPathProvider = new DefaultClassLoaderProvider();
this.classPathProvider = new FactoryClassLoader().createClassLoaderProvider();

this.addHandlers();
}
Expand Down Expand Up @@ -116,5 +116,5 @@ private synchronized void send(String requestId, MessageHandler<?, ?> marshaller
private final ManagedChannel channel;
private final AtomicReference<StreamingMessagePeer> peer;
private final Map<StreamingMessage.ContentCase, Supplier<MessageHandler<?, ?>>> handlerSuppliers;
private final DefaultClassLoaderProvider classPathProvider;
private final ClassLoaderProvider classPathProvider;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.microsoft.azure.functions.worker.broker;

import java.lang.reflect.*;
import java.util.*;

import com.microsoft.azure.functions.worker.binding.*;
import com.microsoft.azure.functions.worker.description.*;
import com.microsoft.azure.functions.worker.reflect.*;
import com.microsoft.azure.functions.rpc.messages.*;

/**
* Used to executor of arbitrary Java method in any JAR using reflection.
* Thread-Safety: Multiple thread.
*/
public class EnhancedJavaMethodExecutorImpl implements JavaMethodExecutor {
public EnhancedJavaMethodExecutorImpl(FunctionMethodDescriptor descriptor, Map<String, BindingInfo> bindingInfos, ClassLoaderProvider classLoaderProvider)
throws ClassNotFoundException, NoSuchMethodException
{
descriptor.validateMethodInfo();

this.classLoader = classLoaderProvider.createClassLoader();
this.containingClass = getContainingClass(descriptor.getFullClassName());
this.overloadResolver = new ParameterResolver();

for (Method method : this.containingClass.getMethods()) {
if (method.getName().equals(descriptor.getMethodName())) {
this.overloadResolver.addCandidate(method);
}
}

if (!this.overloadResolver.hasCandidates()) {
throw new NoSuchMethodException("There are no methods named \"" + descriptor.getName() + "\" in class \"" + descriptor.getFullClassName() + "\"");
}

if (this.overloadResolver.hasMultipleCandidates()) {
throw new UnsupportedOperationException("Found more than one function with method name \"" + descriptor.getName() + "\" in class \"" + descriptor.getFullClassName() + "\"");
}

this.bindingDefinitions = new HashMap<>();

for (Map.Entry<String, BindingInfo> entry : bindingInfos.entrySet()) {
this.bindingDefinitions.put(entry.getKey(), new BindingDefinition(entry.getKey(), entry.getValue()));
}
}

public Map<String, BindingDefinition> getBindingDefinitions() { return this.bindingDefinitions; }

public ParameterResolver getOverloadResolver() { return this.overloadResolver; }

public void execute(BindingDataStore dataStore) throws Exception {
try {
Thread.currentThread().setContextClassLoader(this.classLoader);
Object retValue = this.overloadResolver.resolve(dataStore)
.orElseThrow(() -> new NoSuchMethodException("Cannot locate the method signature with the given input"))
.invoke(() -> this.containingClass.newInstance());
dataStore.setDataTargetValue(BindingDataStore.RETURN_NAME, retValue);
} finally {
Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
}
}

private Class<?> getContainingClass(String className) throws ClassNotFoundException {
return Class.forName(className, true, this.classLoader);
}

private final Class<?> containingClass;
private final ClassLoader classLoader;
private final ParameterResolver overloadResolver;
private final Map<String, BindingDefinition> bindingDefinitions;
}
Loading