diff --git a/containers/azure-functions-java-8/.devcontainer/Dockerfile b/containers/azure-functions-java-8/.devcontainer/Dockerfile new file mode 100644 index 0000000000..fe788d5424 --- /dev/null +++ b/containers/azure-functions-java-8/.devcontainer/Dockerfile @@ -0,0 +1,69 @@ +#----------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See LICENSE in the project root for license information. +#----------------------------------------------------------------------------------------- + +FROM maven:3-jdk-8 + +# Configure apt +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update \ + && apt-get -y install --no-install-recommends apt-utils 2>&1 + +# Install dotnet core sdk - See https://github.com/dotnet/dotnet-docker/blob/master/2.1/sdk/stretch/amd64/Dockerfile +# Install .NET CLI dependencies +RUN apt-get install -y --no-install-recommends \ + libc6 \ + libgcc1 \ + libgssapi-krb5-2 \ + libicu57 \ + liblttng-ust0 \ + libssl1.0.2 \ + libstdc++6 \ + zlib1g + +# Install .NET Core SDK +ENV DOTNET_SDK_VERSION 2.1.603 + +RUN curl -SL --output dotnet.tar.gz https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz \ + && dotnet_sha512='dd0efb8aae75d8f48ef3abbeca38ae14d2621a47e37b2d9d74755b58f9173343305f1a62cfa9a03f17c42f58b1d1b653d271e7d1327c81ff4af0a54c43c7db59' \ + && echo "$dotnet_sha512 dotnet.tar.gz" | sha512sum -c - \ + && mkdir -p /usr/share/dotnet \ + && tar -zxf dotnet.tar.gz -C /usr/share/dotnet \ + && rm dotnet.tar.gz \ + && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet + +# Configure web servers to bind to port 80 when present +ENV ASPNETCORE_URLS=http://+:80 \ + # Enable detection of running in a container + DOTNET_RUNNING_IN_CONTAINER=true \ + # Enable correct mode for dotnet watch (only mode supported in a container) + DOTNET_USE_POLLING_FILE_WATCHER=true \ + # Skip extraction of XML docs - generally not useful within an image/container - helps performance + NUGET_XMLDOC_MODE=skip + +# Trigger first run experience by running arbitrary cmd to populate local package cache +RUN dotnet help &> /dev/null + +# Verify git and needed tools are installed +RUN apt-get -y install \ + git \ + procps \ + curl \ + apt-transport-https \ + gnupg2 \ + lsb-release + +# Install Azure Functions and Azure CLI +RUN echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/azure-cli.list \ + && echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-$(lsb_release -cs)-prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list \ + && curl -sL https://packages.microsoft.com/keys/microsoft.asc | apt-key add - 2>/dev/null \ + && apt-get update \ + && apt-get install -y azure-cli azure-functions-core-tools + +# Clean up +RUN apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* +ENV DEBIAN_FRONTEND=dialog + diff --git a/containers/azure-functions-java-8/.devcontainer/devcontainer.json b/containers/azure-functions-java-8/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..a6eeee6538 --- /dev/null +++ b/containers/azure-functions-java-8/.devcontainer/devcontainer.json @@ -0,0 +1,10 @@ +{ + "name": "Azure Functions & Java 8", + "dockerFile": "Dockerfile", + "appPort": 7071, + "extensions": [ + "ms-azuretools.vscode-azurefunctions", + "vscjava.vscode-java-pack", + "redhat.vscode-xml" + ] +} \ No newline at end of file diff --git a/containers/azure-functions-java-8/.npmignore b/containers/azure-functions-java-8/.npmignore new file mode 100644 index 0000000000..1d72d293eb --- /dev/null +++ b/containers/azure-functions-java-8/.npmignore @@ -0,0 +1,4 @@ +README.md +test-project +.vscode +.npmignore diff --git a/containers/azure-functions-java-8/.vscode/extensions.json b/containers/azure-functions-java-8/.vscode/extensions.json new file mode 100644 index 0000000000..f0557ab110 --- /dev/null +++ b/containers/azure-functions-java-8/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-azuretools.vscode-azurefunctions", + "vscjava.vscode-java-debug" + ] +} diff --git a/containers/azure-functions-java-8/.vscode/launch.json b/containers/azure-functions-java-8/.vscode/launch.json new file mode 100644 index 0000000000..fa096b1d72 --- /dev/null +++ b/containers/azure-functions-java-8/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Java Functions", + "type": "java", + "request": "attach", + "hostName": "127.0.0.1", + "port": 5005, + "preLaunchTask": "func: host start" + } + ] +} diff --git a/containers/azure-functions-java-8/.vscode/settings.json b/containers/azure-functions-java-8/.vscode/settings.json new file mode 100644 index 0000000000..475d59af10 --- /dev/null +++ b/containers/azure-functions-java-8/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "azureFunctions.projectRuntime": "~2", + "azureFunctions.projectLanguage": "Java", + "azureFunctions.deploySubpath": "target/azure-functions/myapp-20190424131606275/", + "azureFunctions.preDeployTask": "package", + "debug.internalConsoleOptions": "neverOpen" +} diff --git a/containers/azure-functions-java-8/.vscode/tasks.json b/containers/azure-functions-java-8/.vscode/tasks.json new file mode 100644 index 0000000000..0254ca3d9d --- /dev/null +++ b/containers/azure-functions-java-8/.vscode/tasks.json @@ -0,0 +1,20 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "func", + "command": "host start", + "problemMatcher": "$func-watch", + "isBackground": true, + "options": { + "cwd": "${workspaceFolder}/test-project/target/azure-functions/myapp-20190424131606275/" + }, + "dependsOn": "package" + }, + { + "label": "package", + "command": "cd ${workspaceFolder}/test-project && mvn clean package", + "type": "shell" + } + ] +} diff --git a/containers/azure-functions-java-8/README.md b/containers/azure-functions-java-8/README.md new file mode 100644 index 0000000000..07f0ca794c --- /dev/null +++ b/containers/azure-functions-java-8/README.md @@ -0,0 +1,49 @@ +# Azure Functions & Java 8 + +## Summary + +*Develop Azure Functions in Java. Includes JDK 8, Maven, XML tools, the Azure Functions SDK, and related extensions and dependencies.* + +| Metadata | Value | +|----------|-------| +| *Contributors* | The VS Code Java Team | +| *Definition type* | Dockerfile | +| *Languages, platforms* | Azure Functions, Java | + +## Using this definition with an existing folder + +This definition requires an Azure subscription to use. You can create a [free account here](https://azure.microsoft.com/en-us/free/serverless/), learn more about using [Azure Functions with VS Code](https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-function-vs-code) and [Java Azure Functions with VS Code](https://code.visualstudio.com/docs/java/java-azurefunctions) here. Once you have an Azure account, follow these steps: + +1. If this is your first time using a development container, please follow the [getting started steps](https://aka.ms/vscode-remote/containers/getting-started) to set up your machine. + +2. To use VS Code's copy of this definition: + 1. Start VS Code and open your project folder. + 2. Press F1 select and **Remote-Containers: Create Container Configuration File...** from the command palette. + 3. Select the Azure Functions & Java 8 definition. + +3. To use latest-and-greatest copy of this definition from the repository: + 1. Clone this repository. + 2. Copy the contents of `containers/azure-functions-java-8/.devcontainer` to the root of your project folder. + 3. Start VS Code and open your project folder. + +4. After following step 2 or 3, the contents of the `.devcontainer` folder in your project can be adapted to meet your needs. + +5. Finally, press F1 and run **Remote-Containers: Reopen Folder in Container** to start using the definition. + +## Testing the definition + +This definition includes some test code that will help you verify it is working as expected on your system. Follow these steps: + +1. If this is your first time using a development container, please follow the [getting started steps](https://aka.ms/vscode-remote/containers/getting-started) to set up your machine. +2. Clone this repository. +3. Start VS Code, press F1, and select **Remote-Containers: Open Folder in Container...** +4. Select the `containers/azure-functions-java-8` folder. +5. After the folder has opened in the container, press F5 to start the project. +6. After the debugger is started, type `curl http://localhost:7071/api/HttpTrigger-Java?name=test` in the terminal, you should see "Hello, test" echoed by the Azure Function. +7. From here, you can add breakpoints or edit the contents of the `test-project` folder to do further testing. + +## License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the MIT License. See [LICENSE](https://github.com/Microsoft/vscode-dev-containers/blob/master/LICENSE). diff --git a/containers/azure-functions-java-8/test-project/.classpath b/containers/azure-functions-java-8/test-project/.classpath new file mode 100644 index 0000000000..f37606259e --- /dev/null +++ b/containers/azure-functions-java-8/test-project/.classpath @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/containers/azure-functions-java-8/test-project/.gitignore b/containers/azure-functions-java-8/test-project/.gitignore new file mode 100644 index 0000000000..a6320ebc94 --- /dev/null +++ b/containers/azure-functions-java-8/test-project/.gitignore @@ -0,0 +1,35 @@ +# Build output +target/ +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# IDE +.idea/ +*.iml + +# macOS +.DS_Store + +# Azure Functions +local.settings.json +bin/ +obj/ diff --git a/containers/azure-functions-java-8/test-project/.project b/containers/azure-functions-java-8/test-project/.project new file mode 100644 index 0000000000..9675576cc2 --- /dev/null +++ b/containers/azure-functions-java-8/test-project/.project @@ -0,0 +1,23 @@ + + + myapp + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/containers/azure-functions-java-8/test-project/.settings/org.eclipse.core.resources.prefs b/containers/azure-functions-java-8/test-project/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..4c28b1a898 --- /dev/null +++ b/containers/azure-functions-java-8/test-project/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding/=UTF-8 diff --git a/containers/azure-functions-java-8/test-project/.settings/org.eclipse.jdt.apt.core.prefs b/containers/azure-functions-java-8/test-project/.settings/org.eclipse.jdt.apt.core.prefs new file mode 100644 index 0000000000..ec0c557e52 --- /dev/null +++ b/containers/azure-functions-java-8/test-project/.settings/org.eclipse.jdt.apt.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.apt.aptEnabled=false diff --git a/containers/azure-functions-java-8/test-project/.settings/org.eclipse.jdt.core.prefs b/containers/azure-functions-java-8/test-project/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..3a7f4f9433 --- /dev/null +++ b/containers/azure-functions-java-8/test-project/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.processAnnotations=disabled +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/containers/azure-functions-java-8/test-project/.settings/org.eclipse.m2e.core.prefs b/containers/azure-functions-java-8/test-project/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000000..14b697b7bb --- /dev/null +++ b/containers/azure-functions-java-8/test-project/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/containers/azure-functions-java-8/test-project/host.json b/containers/azure-functions-java-8/test-project/host.json new file mode 100644 index 0000000000..a8fe8c4c8e --- /dev/null +++ b/containers/azure-functions-java-8/test-project/host.json @@ -0,0 +1,3 @@ +{ + "version": "2.0" +} diff --git a/containers/azure-functions-java-8/test-project/pom.xml b/containers/azure-functions-java-8/test-project/pom.xml new file mode 100644 index 0000000000..0df9100b7d --- /dev/null +++ b/containers/azure-functions-java-8/test-project/pom.xml @@ -0,0 +1,208 @@ + + + 4.0.0 + + com.mycompany.demo + myapp + 1.0 + jar + + Azure Java Functions + + + UTF-8 + 1.8 + 1.8 + 1.3.1 + 1.3.0 + myapp-20190424131606275 + westus + ${project.build.directory}/azure-functions/${functionAppName} + java-functions-group + + + + + maven.snapshots + Maven Central Snapshot Repository + https://oss.sonatype.org/content/repositories/snapshots/ + + false + + + true + + + + + + + maven.snapshots + Maven Central Snapshot Repository + https://oss.sonatype.org/content/repositories/snapshots/ + + false + + + true + + + + + + + + junit + junit + 4.12 + + + org.mockito + mockito-core + 2.4.0 + + + com.microsoft.azure.functions + azure-functions-java-library + ${azure.functions.java.library.version} + + + + + + + com.microsoft.azure.functions + azure-functions-java-library + + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + + + + + + com.microsoft.azure + azure-functions-maven-plugin + ${azure.functions.maven.plugin.version} + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + + + + + com.microsoft.azure + azure-functions-maven-plugin + + ${functionResourceGroup} + ${functionAppName} + ${functionAppRegion} + + + + WEBSITE_RUN_FROM_PACKAGE + 1 + + + FUNCTIONS_EXTENSION_VERSION + ~2 + + + FUNCTIONS_WORKER_RUNTIME + java + + + + + + package-functions + + package + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-resources + package + + copy-resources + + + true + ${stagingDirectory} + + + ${project.basedir} + + host.json + local.settings.json + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + prepare-package + + copy-dependencies + + + ${stagingDirectory}/lib + false + false + true + runtime + azure-functions-java-library + + + + + + + maven-clean-plugin + 3.1.0 + + + + obj + + + + + + + + diff --git a/containers/azure-functions-java-8/test-project/src/main/java/com/mycompany/app/Function.java b/containers/azure-functions-java-8/test-project/src/main/java/com/mycompany/app/Function.java new file mode 100644 index 0000000000..fa53a9cd98 --- /dev/null +++ b/containers/azure-functions-java-8/test-project/src/main/java/com/mycompany/app/Function.java @@ -0,0 +1,34 @@ +package com.mycompany.app; + +import java.util.*; +import com.microsoft.azure.functions.annotation.*; +import com.microsoft.azure.functions.*; + +/** + * Azure Functions with HTTP Trigger. + */ +public class Function { + /** + * This function listens at endpoint "/api/HttpTrigger-Java". Two ways to invoke it using "curl" command in bash: + * 1. curl -d "HTTP Body" {your host}/api/HttpTrigger-Java&code={your function key} + * 2. curl "{your host}/api/HttpTrigger-Java?name=HTTP%20Query&code={your function key}" + * Function Key is not needed when running locally, it is used to invoke function deployed to Azure. + * More details: https://aka.ms/functions_authorization_keys + */ + @FunctionName("HttpTrigger-Java") + public HttpResponseMessage run( + @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage> request, + final ExecutionContext context) { + context.getLogger().info("Java HTTP trigger processed a request."); + + // Parse query parameter + String query = request.getQueryParameters().get("name"); + String name = request.getBody().orElse(query); + + if (name == null) { + return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build(); + } else { + return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build(); + } + } +} diff --git a/containers/azure-functions-java-8/test-project/src/test/java/com/mycompany/app/FunctionTest.java b/containers/azure-functions-java-8/test-project/src/test/java/com/mycompany/app/FunctionTest.java new file mode 100644 index 0000000000..b8bee2f3ec --- /dev/null +++ b/containers/azure-functions-java-8/test-project/src/test/java/com/mycompany/app/FunctionTest.java @@ -0,0 +1,58 @@ +package com.mycompany.app; + +import org.junit.Test; + +import com.microsoft.azure.functions.*; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Logger; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + + +/** + * Unit test for Function class. + */ +public class FunctionTest { + /** + * Unit test for HttpTriggerJava method. + */ + @Test + public void testHttpTriggerJava() throws Exception { + // Setup + @SuppressWarnings("unchecked") + final HttpRequestMessage> req = mock(HttpRequestMessage.class); + + final Map queryParams = new HashMap<>(); + queryParams.put("name", "Azure"); + doReturn(queryParams).when(req).getQueryParameters(); + + final Optional queryBody = Optional.empty(); + doReturn(queryBody).when(req).getBody(); + + doAnswer(new Answer() { + @Override + public HttpResponseMessage.Builder answer(InvocationOnMock invocation) { + HttpStatus status = (HttpStatus) invocation.getArguments()[0]; + return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status); + } + }).when(req).createResponseBuilder(any(HttpStatus.class)); + + final ExecutionContext context = mock(ExecutionContext.class); + doReturn(Logger.getGlobal()).when(context).getLogger(); + + // Invoke + final HttpResponseMessage ret = new Function().run(req, context); + + // Verify + assertEquals(ret.getStatus(), HttpStatus.OK); + } +} diff --git a/containers/azure-functions-java-8/test-project/src/test/java/com/mycompany/app/HttpResponseMessageMock.java b/containers/azure-functions-java-8/test-project/src/test/java/com/mycompany/app/HttpResponseMessageMock.java new file mode 100644 index 0000000000..482f3f5830 --- /dev/null +++ b/containers/azure-functions-java-8/test-project/src/test/java/com/mycompany/app/HttpResponseMessageMock.java @@ -0,0 +1,80 @@ +package com.mycompany.app; + +import com.microsoft.azure.functions.*; + +import java.util.Map; + +/** + * The mock for HttpResponseMessage, can be used in unit tests to verify if the + * returned response by HTTP trigger function is correct or not. + */ +public class HttpResponseMessageMock implements HttpResponseMessage { + private int httpStatusCode; + private HttpStatusType httpStatus; + private Object body; + private Map headers; + + public HttpResponseMessageMock(HttpStatusType status, Map headers, Object body) { + this.httpStatus = status; + this.httpStatusCode = status.value(); + this.headers = headers; + this.body = body; + } + + @Override + public HttpStatusType getStatus() { + return this.httpStatus; + } + + @Override + public int getStatusCode() { + return httpStatusCode; + } + + @Override + public String getHeader(String key) { + return this.headers.get(key); + } + + @Override + public Object getBody() { + return this.body; + } + + public static class HttpResponseMessageBuilderMock implements HttpResponseMessage.Builder { + private Object body; + private int httpStatusCode; + private Map headers; + private HttpStatusType httpStatus; + + public Builder status(HttpStatus status) { + this.httpStatusCode = status.value(); + this.httpStatus = status; + return this; + } + + @Override + public Builder status(HttpStatusType httpStatusType) { + this.httpStatusCode = httpStatusType.value(); + this.httpStatus = httpStatusType; + return this; + } + + @Override + public HttpResponseMessage.Builder header(String key, String value) { + this.headers.put(key, value); + return this; + } + + @Override + public HttpResponseMessage.Builder body(Object body) { + this.body = body; + return this; + } + + @Override + public HttpResponseMessage build() { + return new HttpResponseMessageMock(this.httpStatus, this.headers, this.body); + } + } +}