├── .github
├── .release-please-manifest.json
├── blunderbuss.yml
├── release-please-config.json
├── release-please.yml
├── release-trigger.yml
├── renovate.json
└── workflows
│ ├── buildpack-integration-test.yml
│ ├── codeql.yml
│ ├── conformance.yaml
│ ├── lint.yaml
│ ├── scorecard.yml
│ └── unit.yaml
├── .gitignore
├── .kokoro
├── release.cfg
└── release.sh
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── function-maven-plugin
├── CHANGELOG.md
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── google
│ │ └── cloud
│ │ └── functions
│ │ └── plugin
│ │ ├── DeployFunction.java
│ │ └── RunFunction.java
│ └── test
│ └── java
│ └── com
│ └── google
│ └── cloud
│ └── functions
│ └── plugin
│ └── DeployFunctionTest.java
├── functions-framework-api
├── CHANGELOG.md
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── google
│ └── cloud
│ └── functions
│ ├── BackgroundFunction.java
│ ├── CloudEventsFunction.java
│ ├── Context.java
│ ├── HttpFunction.java
│ ├── HttpMessage.java
│ ├── HttpRequest.java
│ ├── HttpResponse.java
│ ├── RawBackgroundFunction.java
│ └── TypedFunction.java
├── invoker
├── CHANGELOG.md
├── conformance
│ ├── buildpack_pom.xml
│ ├── pom.xml
│ ├── prerun.sh
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── google
│ │ └── cloud
│ │ └── functions
│ │ └── conformance
│ │ ├── BackgroundEventConformanceFunction.java
│ │ ├── CloudEventsConformanceFunction.java
│ │ ├── ConcurrentHttpConformanceFunction.java
│ │ ├── HttpConformanceFunction.java
│ │ └── TypedConformanceFunction.java
├── core
│ ├── pom.xml
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── google
│ │ │ └── cloud
│ │ │ └── functions
│ │ │ └── invoker
│ │ │ ├── BackgroundFunctionExecutor.java
│ │ │ ├── CloudEvents.java
│ │ │ ├── CloudFunctionsContext.java
│ │ │ ├── Event.java
│ │ │ ├── GcfEvents.java
│ │ │ ├── HttpFunctionExecutor.java
│ │ │ ├── TypedFunctionExecutor.java
│ │ │ ├── gcf
│ │ │ ├── ExecutionIdUtil.java
│ │ │ └── JsonLogHandler.java
│ │ │ ├── http
│ │ │ ├── HttpRequestImpl.java
│ │ │ ├── HttpResponseImpl.java
│ │ │ └── TimeoutFilter.java
│ │ │ └── runner
│ │ │ └── Invoker.java
│ │ └── test
│ │ ├── java
│ │ ├── PackagelessHelloWorld.java
│ │ └── com
│ │ │ └── google
│ │ │ └── cloud
│ │ │ └── functions
│ │ │ └── invoker
│ │ │ ├── BackgroundFunctionExecutorTest.java
│ │ │ ├── CloudEventsTest.java
│ │ │ ├── GcfEventsTest.java
│ │ │ ├── HttpFunctionExecutorTest.java
│ │ │ ├── IntegrationTest.java
│ │ │ ├── TypedFunctionExecutorTest.java
│ │ │ ├── http
│ │ │ └── HttpTest.java
│ │ │ ├── runner
│ │ │ └── InvokerTest.java
│ │ │ └── testfunctions
│ │ │ ├── BackgroundSnoop.java
│ │ │ ├── CloudEventSnoop.java
│ │ │ ├── Echo.java
│ │ │ ├── EchoUrl.java
│ │ │ ├── ExceptionBackground.java
│ │ │ ├── ExceptionHttp.java
│ │ │ ├── HelloWorld.java
│ │ │ ├── Log.java
│ │ │ ├── Multipart.java
│ │ │ ├── Nested.java
│ │ │ ├── TimeoutHttp.java
│ │ │ ├── Typed.java
│ │ │ ├── TypedBackgroundSnoop.java
│ │ │ ├── TypedCustomFormat.java
│ │ │ └── TypedVoid.java
│ │ └── resources
│ │ ├── adder_gcf_ga_event.json
│ │ ├── firebase-auth-cloudevent-input.json
│ │ ├── firebase-auth-legacy-output.json
│ │ ├── firebase-auth1.json
│ │ ├── firebase-auth2.json
│ │ ├── firebase-db1-cloudevent-input.json
│ │ ├── firebase-db1-legacy-output.json
│ │ ├── firebase-db1.json
│ │ ├── firebase-db2-cloudevent-input.json
│ │ ├── firebase-db2-legacy-output.json
│ │ ├── firebase-db2.json
│ │ ├── firestore_complex-cloudevent-input.json
│ │ ├── firestore_complex-legacy-output.json
│ │ ├── firestore_complex.json
│ │ ├── firestore_simple.json
│ │ ├── legacy_pubsub.json
│ │ ├── legacy_storage_change.json
│ │ ├── pubsub_background.json
│ │ ├── pubsub_binary.json
│ │ ├── pubsub_emulator.json
│ │ ├── pubsub_text-cloudevent-input.json
│ │ ├── pubsub_text-legacy-output.json
│ │ ├── pubsub_text.json
│ │ ├── storage-cloudevent-input.json
│ │ ├── storage-legacy-output.json
│ │ ├── storage.json
│ │ └── typed_nameconcat_request.json
├── pom.xml
└── testfunction
│ ├── pom.xml
│ └── src
│ └── test
│ └── java
│ └── com
│ └── example
│ └── functionjar
│ ├── Background.java
│ ├── Checker.java
│ ├── Foreground.java
│ └── Typed.java
└── run_conformance_tests.sh
/.github/.release-please-manifest.json:
--------------------------------------------------------------------------------
1 | {"functions-framework-api":"1.1.4","invoker":"1.4.1","function-maven-plugin":"0.11.1"}
2 |
--------------------------------------------------------------------------------
/.github/blunderbuss.yml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.github/release-please-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "separate-pull-requests": true,
3 | "packages": {
4 | "functions-framework-api": {
5 | "release-type": "maven",
6 | "component": "functions-framework-api",
7 | "extra-files": [
8 | {
9 | "type": "xml",
10 | "path": "pom.xml",
11 | "xpath": "//*[local-name()='artifactId' and text()='functions-framework-api']/parent::*/*[local-name()='version']"
12 | }
13 | ]
14 | },
15 | "invoker": {
16 | "release-type": "maven",
17 | "component": "java-function-invoker",
18 | "extra-files": [
19 | {
20 | "type": "xml",
21 | "path": "pom.xml",
22 | "xpath": "//*[local-name()='artifactId' and text()='java-function-invoker-parent']/parent::*/*[local-name()='version']"
23 | },
24 | {
25 | "type": "xml",
26 | "path": "core/pom.xml",
27 | "xpath": "//*[local-name()='artifactId' and text()='java-function-invoker-parent']/parent::*/*[local-name()='version']"
28 | },
29 | {
30 | "type": "xml",
31 | "path": "core/pom.xml",
32 | "xpath": "//*[local-name()='artifactId' and text()='java-function-invoker']/parent::*/*[local-name()='version']"
33 | },
34 | {
35 | "type": "xml",
36 | "path": "core/pom.xml",
37 | "xpath": "//*[local-name()='artifactId' and text()='java-function-invoker-testfunction']/parent::*/*[local-name()='version']"
38 | },
39 | {
40 | "type": "xml",
41 | "path": "conformance/pom.xml",
42 | "xpath": "//*[local-name()='artifactId' and text()='java-function-invoker-parent']/parent::*/*[local-name()='version']"
43 | },
44 | {
45 | "type": "xml",
46 | "path": "conformance/pom.xml",
47 | "xpath": "//*[local-name()='artifactId' and text()='conformance']/parent::*/*[local-name()='version']"
48 | },
49 | {
50 | "type": "xml",
51 | "path": "testfunction/pom.xml",
52 | "xpath": "//*[local-name()='artifactId' and text()='java-function-invoker-parent']/parent::*/*[local-name()='version']"
53 | },
54 | {
55 | "type": "xml",
56 | "path": "testfunction/pom.xml",
57 | "xpath": "//*[local-name()='artifactId' and text()='java-function-invoker-testfunction']/parent::*/*[local-name()='version']"
58 | }
59 | ]
60 | },
61 | "function-maven-plugin": {
62 | "release-type": "maven",
63 | "component": "function-maven-plugin",
64 | "extra-files": [
65 | {
66 | "type": "xml",
67 | "path": "pom.xml",
68 | "xpath": "//*[local-name()='artifactId' and text()='function-maven-plugin']/parent::*/*[local-name()='version']"
69 | }
70 | ]
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/.github/release-please.yml:
--------------------------------------------------------------------------------
1 | handleGHRelease: true
2 | monorepoTags: true
3 | manifest: true
4 | manifestConfig: '.github/release-please-config.json'
5 | manifestFile: '.github/.release-please-manifest.json'
--------------------------------------------------------------------------------
/.github/release-trigger.yml:
--------------------------------------------------------------------------------
1 | enabled: true
2 | multiScmName: functions-framework-java
3 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["group:allNonMajor", "schedule:monthly"],
4 | "ignoreDeps": ["org.apache.maven.plugins:maven-source-plugin"],
5 | "packageRules": [
6 | {
7 | "description": "Create a PR whenever there is a new major version",
8 | "matchUpdateTypes": [
9 | "major"
10 | ]
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/.github/workflows/buildpack-integration-test.yml:
--------------------------------------------------------------------------------
1 | # Validates Functions Framework with GCF buildpacks.
2 | name: Buildpack Integration Test
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | workflow_dispatch:
9 | # Runs every day on 12:00 AM PST
10 | schedule:
11 | - cron: "0 0 * * *"
12 |
13 | # Declare default permissions as read only.
14 | permissions: read-all
15 |
16 | jobs:
17 | java11-buildpack-test:
18 | uses: GoogleCloudPlatform/functions-framework-conformance/.github/workflows/buildpack-integration-test.yml@main
19 | with:
20 | http-builder-source: '/tmp/tests/conformance'
21 | http-builder-target: 'com.google.cloud.functions.conformance.HttpConformanceFunction'
22 | cloudevent-builder-source: '/tmp/tests/conformance'
23 | cloudevent-builder-target: 'com.google.cloud.functions.conformance.CloudEventsConformanceFunction'
24 | prerun: 'invoker/conformance/prerun.sh'
25 | builder-runtime: 'java11'
26 | builder-runtime-version: '11'
27 | builder-url: gcr.io/serverless-runtimes/google-22-full/builder/java:latest
28 | java17-buildpack-test:
29 | uses: GoogleCloudPlatform/functions-framework-conformance/.github/workflows/buildpack-integration-test.yml@main
30 | with:
31 | http-builder-source: '/tmp/tests/conformance'
32 | http-builder-target: 'com.google.cloud.functions.conformance.HttpConformanceFunction'
33 | cloudevent-builder-source: '/tmp/tests/conformance'
34 | cloudevent-builder-target: 'com.google.cloud.functions.conformance.CloudEventsConformanceFunction'
35 | prerun: 'invoker/conformance/prerun.sh'
36 | builder-runtime: 'java17'
37 | builder-runtime-version: '17'
38 | builder-url: gcr.io/serverless-runtimes/google-22-full/builder/java:latest
39 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | # The branches below must be a subset of the branches above
8 | branches: [ "main" ]
9 |
10 | permissions:
11 | contents: read
12 |
13 | jobs:
14 | analyze:
15 | name: Analyze
16 | runs-on: ubuntu-latest
17 |
18 | permissions:
19 | actions: read
20 | contents: read
21 | security-events: write
22 |
23 | strategy:
24 | fail-fast: false
25 | matrix:
26 | # Autobuild each of these seperate maven projects
27 | working-directory: ['invoker', 'functions-framework-api', 'function-maven-plugin']
28 |
29 | steps:
30 | - name: Harden Runner
31 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
32 | with:
33 | disable-sudo: true
34 | egress-policy: block
35 | allowed-endpoints: >
36 | api.github.com:443
37 | github.com:443
38 | objects.githubusercontent.com:443
39 | proxy.golang.org:443
40 | repo.maven.apache.org:443
41 | storage.googleapis.com:443
42 | uploads.github.com:443
43 |
44 | - name: Checkout repository
45 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
46 |
47 | # Initializes the CodeQL tools for scanning.
48 | - name: Initialize CodeQL
49 | uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
50 | with:
51 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
52 | languages: java
53 | # If you wish to specify custom queries, you can do so here or in a config file.
54 | # By default, queries listed here will override any specified in a config file.
55 | # Prefix the list here with "+" to use these queries and those in the config file.
56 |
57 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
58 | # queries: security-extended,security-and-quality
59 |
60 |
61 |
62 | - name: Build
63 | run: |
64 | (cd functions-framework-api/ && mvn install)
65 | (cd invoker/ && mvn clean install)
66 | (cd function-maven-plugin && mvn install)
67 |
68 | - name: Perform CodeQL Analysis
69 | uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
70 | with:
71 | category: ${{ matrix.working-directory }}
72 |
--------------------------------------------------------------------------------
/.github/workflows/conformance.yaml:
--------------------------------------------------------------------------------
1 | name: Java Conformance CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 |
8 | # Declare default permissions as read only.
9 | permissions: read-all
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | java: [
17 | 11.x,
18 | ]
19 | steps:
20 | - name: Harden Runner
21 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
22 | with:
23 | disable-sudo: true
24 | egress-policy: block
25 | allowed-endpoints: >
26 | api.github.com:443
27 | github.com:443
28 | objects.githubusercontent.com:443
29 | proxy.golang.org:443
30 | repo.maven.apache.org:443
31 | storage.googleapis.com:443
32 |
33 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
34 |
35 | - name: Set up JDK ${{ matrix.java }}
36 | uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
37 | with:
38 | java-version: ${{ matrix.java }}
39 | distribution: temurin
40 |
41 | - name: Setup Go
42 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
43 | with:
44 | go-version: '1.21'
45 |
46 | - name: Build API with Maven
47 | run: (cd functions-framework-api/ && mvn install)
48 |
49 | - name: Build invoker with Maven
50 | run: (cd invoker/ && mvn install)
51 |
52 | - name: Build invoker Maven Plugin
53 | run: (cd function-maven-plugin/ && mvn install)
54 |
55 | - name: Run HTTP conformance tests
56 | uses: GoogleCloudPlatform/functions-framework-conformance/action@1041a97e93a463d9efb17dda821f3ddc0bf0024f # main
57 | with:
58 | functionType: 'http'
59 | useBuildpacks: false
60 | cmd: "'mvn -f invoker/conformance/pom.xml function:run -Drun.functionTarget=com.google.cloud.functions.conformance.HttpConformanceFunction'"
61 | startDelay: 10
62 |
63 | - name: Run Typed conformance tests
64 | uses: GoogleCloudPlatform/functions-framework-conformance/action@1041a97e93a463d9efb17dda821f3ddc0bf0024f # main
65 | with:
66 | functionType: 'http'
67 | declarativeType: 'typed'
68 | useBuildpacks: false
69 | cmd: "'mvn -f invoker/conformance/pom.xml function:run -Drun.functionTarget=com.google.cloud.functions.conformance.TypedConformanceFunction'"
70 | startDelay: 10
71 |
72 | - name: Run background event conformance tests
73 | uses: GoogleCloudPlatform/functions-framework-conformance/action@1041a97e93a463d9efb17dda821f3ddc0bf0024f # main
74 | with:
75 | functionType: 'legacyevent'
76 | useBuildpacks: false
77 | validateMapping: true
78 | cmd: "'mvn -f invoker/conformance/pom.xml function:run -Drun.functionTarget=com.google.cloud.functions.conformance.BackgroundEventConformanceFunction'"
79 | startDelay: 10
80 |
81 | - name: Run cloudevent conformance tests
82 | uses: GoogleCloudPlatform/functions-framework-conformance/action@1041a97e93a463d9efb17dda821f3ddc0bf0024f # main
83 | with:
84 | functionType: 'cloudevent'
85 | useBuildpacks: false
86 | validateMapping: true
87 | cmd: "'mvn -f invoker/conformance/pom.xml function:run -Drun.functionTarget=com.google.cloud.functions.conformance.CloudEventsConformanceFunction'"
88 | startDelay: 10
89 |
90 | - name: Run HTTP concurrency conformance tests
91 | uses: GoogleCloudPlatform/functions-framework-conformance/action@1041a97e93a463d9efb17dda821f3ddc0bf0024f # main
92 | with:
93 | functionType: 'http'
94 | useBuildpacks: false
95 | validateConcurrency: true
96 | cmd: "'mvn -f invoker/conformance/pom.xml function:run -Drun.functionTarget=com.google.cloud.functions.conformance.ConcurrentHttpConformanceFunction'"
97 | startDelay: 10
--------------------------------------------------------------------------------
/.github/workflows/lint.yaml:
--------------------------------------------------------------------------------
1 | name: Java Lint CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | workflow_dispatch:
8 | permissions:
9 | contents: read
10 |
11 | jobs:
12 | lint:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Harden Runner
16 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
17 | with:
18 | disable-sudo: true
19 | egress-policy: block
20 | allowed-endpoints: >
21 | github.com:443
22 | repo.maven.apache.org:443
23 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
24 | - name: Set up JDK
25 | uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
26 | with:
27 | java-version: 11.x
28 | distribution: temurin
29 | - name: Build API with Maven
30 | run: (cd functions-framework-api/ && mvn install)
31 | - name: Lint Functions Framework API
32 | run: (cd functions-framework-api/ && mvn clean verify -DskipTests -P lint)
33 | - name: Build Invoker with Maven
34 | run: (cd functions-framework-api/ && mvn install)
35 | - name: Lint Invoker
36 | run: (cd invoker/ && mvn clean verify -DskipTests -P lint)
37 | formatting:
38 | runs-on: ubuntu-latest
39 | steps:
40 | - name: Harden Runner
41 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
42 | with:
43 | egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
44 |
45 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 # v2 minimum required
46 | - name: Set up JDK
47 | uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
48 | with:
49 | java-version: 17.x
50 | distribution: temurin
51 | - name: Run formatter
52 | id: formatter
53 | uses: axel-op/googlejavaformat-action@dbff853fb823671ec5781365233bf86543b13215 # v3
54 | with:
55 | args: "--replace"
56 | skip-commit: true
57 | - name: Print diffs
58 | run: git --no-pager diff --exit-code
59 |
--------------------------------------------------------------------------------
/.github/workflows/scorecard.yml:
--------------------------------------------------------------------------------
1 | name: Scorecard supply-chain security
2 | on:
3 | # For Branch-Protection check. Only the default branch is supported. See
4 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
5 | branch_protection_rule:
6 | # To guarantee Maintained check is occasionally updated. See
7 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
8 | schedule:
9 | - cron: '0 */12 * * *'
10 | push:
11 | branches: [ "main" ]
12 | workflow_dispatch:
13 |
14 | # Declare default permissions as read only.
15 | permissions: read-all
16 |
17 | jobs:
18 | analysis:
19 | name: Scorecard analysis
20 | runs-on: ubuntu-latest
21 | permissions:
22 | # Needed to upload the results to code-scanning dashboard.
23 | security-events: write
24 | # Needed to publish results and get a badge (see publish_results below).
25 | id-token: write
26 |
27 | steps:
28 | - name: Harden Runner
29 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
30 | with:
31 | disable-sudo: true
32 | egress-policy: block
33 | allowed-endpoints: >
34 | api.osv.dev:443
35 | api.scorecard.dev:443
36 | api.securityscorecards.dev:443
37 | auth.docker.io:443
38 | bestpractices.coreinfrastructure.org:443
39 | github.com:443
40 | index.docker.io:443
41 | oss-fuzz-build-logs.storage.googleapis.com:443
42 | sigstore-tuf-root.storage.googleapis.com:443
43 | www.bestpractices.dev:443
44 | *.sigstore.dev:443
45 | *.github.com:443
46 |
47 | - name: "Checkout code"
48 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
49 | with:
50 | persist-credentials: false
51 |
52 | - name: "Run analysis"
53 | uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
54 | with:
55 | results_file: results.sarif
56 | results_format: sarif
57 | # Public repositories:
58 | # - Publish results to OpenSSF REST API for easy access by consumers
59 | # - Allows the repository to include the Scorecard badge.
60 | # - See https://github.com/ossf/scorecard-action#publishing-results.
61 | publish_results: true
62 |
63 | # Upload the results to GitHub's code scanning dashboard.
64 | - name: "Upload to code-scanning"
65 | uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
66 | with:
67 | sarif_file: results.sarif
68 |
--------------------------------------------------------------------------------
/.github/workflows/unit.yaml:
--------------------------------------------------------------------------------
1 | name: Java Unit CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | permissions:
8 | contents: read
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | strategy:
14 | matrix:
15 | java: [
16 | 11.x,
17 | 17.x,
18 | 21-ea
19 | ]
20 | steps:
21 | - name: Harden Runner
22 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
23 | with:
24 | disable-sudo: true
25 | egress-policy: block
26 | allowed-endpoints: >
27 | github.com:443
28 | repo.maven.apache.org:443
29 | api.adoptium.net:443
30 | *.githubusercontent.com:443
31 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
32 | - name: Set up JDK ${{ matrix.java }}
33 | uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
34 | with:
35 | java-version: ${{ matrix.java }}
36 | distribution: temurin
37 | - name: Build with Maven
38 | run: (cd functions-framework-api/ && mvn install)
39 | - name: Test
40 | run: (cd invoker/ && mvn test)
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.ear
17 | *.zip
18 | *.tar.gz
19 | *.rar
20 |
21 | # Maven
22 | target/
23 | dependency-reduced-pom.xml
24 |
25 | # Gradle
26 | .gradle
27 | .idea/
28 | build/
29 | credentials/
30 | out/
31 | gradlew
32 | gradlew.bat
33 | *.iml
34 | *.properties
35 |
36 | client_secret.json
37 | credentials.json
38 | tokens/
39 |
40 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
41 | hs_err_pid*
42 |
43 | .DS_Store
44 | .settings/
45 | .classpath
46 | .project
47 | properties
48 |
49 | # IDE
50 | .vscode/
51 |
52 | # Conformance testing
53 | function_output.json
54 | serverlog_stderr.txt
55 | serverlog_stdout.txt
56 |
--------------------------------------------------------------------------------
/.kokoro/release.cfg:
--------------------------------------------------------------------------------
1 | build_file: "functions-framework-java/.kokoro/release.sh"
2 |
3 | before_action {
4 | fetch_keystore {
5 | keystore_resource {
6 | keystore_config_id: 75669
7 | keyname: "functions-framework-java-release-bot-sonatype-password"
8 | }
9 | keystore_resource {
10 | keystore_config_id: 70247
11 | keyname: "maven-gpg-pubkeyring"
12 | }
13 | keystore_resource {
14 | keystore_config_id: 70247
15 | keyname: "maven-gpg-keyring"
16 | }
17 | keystore_resource {
18 | keystore_config_id: 70247
19 | keyname: "maven-gpg-passphrase"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.kokoro/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Stop execution when any command fails.
4 | set -e
5 |
6 | # update the Maven version to 3.6.3
7 | pushd /usr/local
8 | wget https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.tar.gz
9 | tar -xvzf apache-maven-3.6.3-bin.tar.gz apache-maven-3.6.3
10 | rm -f /usr/local/apache-maven
11 | ln -s /usr/local/apache-maven-3.6.3 /usr/local/apache-maven
12 | rm apache-maven-3.6.3-bin.tar.gz
13 | popd
14 |
15 |
16 | # Get secrets from keystore and set and environment variables.
17 | setup_environment_secrets() {
18 | export GPG_TTY=$(tty)
19 | export SONATYPE_USERNAME=$(cat ${KOKORO_KEYSTORE_DIR}/75669_functions-framework-java-release-bot-sonatype-password | cut -f1 -d':')
20 | export SONATYPE_PASSWORD=$(cat ${KOKORO_KEYSTORE_DIR}/75669_functions-framework-java-release-bot-sonatype-password | cut -f2 -d':')
21 | export GPG_PASSPHRASE=$(cat ${KOKORO_KEYSTORE_DIR}/70247_maven-gpg-passphrase)
22 |
23 | # Add the key ring files to $GNUPGHOME to verify the GPG credentials.
24 | export GNUPGHOME=/tmp/gpg
25 | mkdir $GNUPGHOME
26 | mv ${KOKORO_KEYSTORE_DIR}/70247_maven-gpg-pubkeyring $GNUPGHOME/pubring.gpg
27 | mv ${KOKORO_KEYSTORE_DIR}/70247_maven-gpg-keyring $GNUPGHOME/secring.gpg
28 | gpg -k
29 | }
30 |
31 | create_settings_xml_file() {
32 | echo "
{@code 21 | *30 | * 31 | * ...and then run using {@code mvn function:run}. Or using properties on the command line, like 32 | * this...22 | * 29 | * }com.google.cloud.functions 23 | *function-maven-plugin 24 | *1.0.0-alpha-2-rc3 25 | *26 | * 28 | *com.example.function.Echo 27 | *
{@code 35 | * mvn com.google.cloud.functions:function:1.0.0-alpha-2-rc3:run \ 36 | * -Drun.functionTarget=com.example.function.Echo 37 | * }38 | */ 39 | @Mojo( 40 | name = "run", 41 | defaultPhase = LifecyclePhase.GENERATE_RESOURCES, 42 | requiresDependencyResolution = ResolutionScope.RUNTIME, 43 | requiresDependencyCollection = ResolutionScope.RUNTIME) 44 | @Execute(phase = LifecyclePhase.COMPILE) 45 | public class RunFunction extends AbstractMojo { 46 | 47 | /** 48 | * The name of the function to run. This is the name of a class that implements one of the 49 | * interfaces in {@code com.google.cloud.functions}. 50 | */ 51 | @Parameter(property = "run.functionTarget") 52 | private String functionTarget; 53 | 54 | /** The port on which the HTTP server wrapping the function should listen. */ 55 | @Parameter(property = "run.port", defaultValue = "8080") 56 | private Integer port; 57 | 58 | /** 59 | * Used to determine what classpath needs to be used to load the function. This parameter is 60 | * injected by Maven and can't be set explicitly in a pom.xml file. 61 | */ 62 | @Parameter(defaultValue = "${project.runtimeClasspathElements}", readonly = true, required = true) 63 | private List
Here is an example of an implementation that accesses the {@code messageId} property from a 24 | * payload that matches a user-defined {@code PubSubMessage} class: 25 | * 27 | * 28 | *
29 | * public class Example implements{@code BackgroundFunction46 | * 47 | * @param} { 30 | * private static final Logger logger = Logger.getLogger(Example.class.getName()); 31 | * 32 | * {@code @Override} 33 | * public void accept(PubSubMessage pubSubMessage, Context context) { 34 | * logger.info("Got messageId " + pubSubMessage.messageId); 35 | * } 36 | * } 37 | * 38 | * // Where PubSubMessage is a user-defined class like this: 39 | * public class PubSubMessage { 40 | * String data; 41 | * {@code Map } attributes; 42 | * String messageId; 43 | * String publishTime; 44 | * } 45 | *
The map returned by this method may be empty but is never null.
61 | *
62 | * @return additional attributes form this event.
63 | */
64 | default Map Here is an example of an implementation that parses the JSON payload using Gson, to access its
23 | * {@code messageId} property:
24 | *
26 | *
27 | * Here is an example of an implementation that deserializes the JSON payload into a Java object
42 | * for simpler access, again using Gson:
43 | *
44 | *
77 | * Content-Type: text/plain
78 | * Some-Header: some value
79 | * Some-Header: another value
80 | *
81 | *
82 | * ...then the returned value will map {@code "Content-Type"} to a one-element list containing
83 | * {@code "text/plain"}, and {@code "Some-Header"} to a two-element list containing {@code "some
84 | * value"} and {@code "another value"}.
85 | *
86 | * @return a map where each key is an HTTP header and the corresponding {@code List} value has one
87 | * element for each occurrence of that header.
88 | */
89 | Map
96 | * Content-Type: text/plain
97 | * Some-Header: some value
98 | * Some-Header: another value
99 | *
100 | *
101 | * ...then {@code getFirstHeader("Some-Header")} will return {@code Optional.of("some value")},
102 | * and {@code getFirstHeader("Another-Header")} will return {@code Optional.empty()}.
103 | *
104 | * @param name an HTTP header name.
105 | * @return the first value of the given header, if present.
106 | */
107 | default Optional
45 | * {@code setStatusCode(400, "Something went wrong")}. The named constants in {@link
46 | * java.net.HttpURLConnection}, such as {@link java.net.HttpURLConnection#HTTP_BAD_REQUEST
47 | * HTTP_BAD_REQUEST}, can be used as an alternative to writing numbers in your source code.
48 | *
49 | * @param code the status code.
50 | * @param message the status message.
51 | */
52 | void setStatusCode(int code, String message);
53 |
54 | /**
55 | * Sets the value to use for the {@code Content-Type} header in the response. This may include a
56 | * character encoding, for example {@code setContentType("text/plain; charset=utf-8")}.
57 | *
58 | * @param contentType the content type.
59 | */
60 | void setContentType(String contentType);
61 |
62 | /**
63 | * Returns the {@code Content-Type} that was previously set by {@link #setContentType}, or by
64 | * {@link #appendHeader} with a header name of {@code Content-Type}. If no {@code Content-Type}
65 | * has been set, returns {@code Optional.empty()}.
66 | *
67 | * @return the content type, if any.
68 | */
69 | Optional
28 | * public class Example implements RawBackgroundFunction {
29 | * private static final Logger logger = Logger.getLogger(Example.class.getName());
30 | *
31 | * {@code @Override}
32 | * public void accept(String json, Context context) {
33 | * JsonObject jsonObject = new Gson().fromJson(json, JsonObject.class);
34 | * JsonElement messageId = jsonObject.get("messageId");
35 | * String messageIdString = messageId.getAsJsonString();
36 | * logger.info("Got messageId " + messageIdString);
37 | * }
38 | * }
39 | *
40 | *
41 | *
45 | * public class Example implements RawBackgroundFunction {
46 | * private static final Logger logger = Logger.getLogger(Example.class.getName());
47 | *
48 | * {@code @Override}
49 | * public void accept(String json, Context context) {
50 | * PubSubMessage message = new Gson().fromJson(json, PubSubMessage.class);
51 | * logger.info("Got messageId " + message.messageId);
52 | * }
53 | * }
54 | *
55 | * // Where PubSubMessage is a user-defined class like this:
56 | * public class PubSubMessage {
57 | * String data;
58 | * {@code Map
63 | */
64 | @FunctionalInterface
65 | public interface RawBackgroundFunction {
66 | /**
67 | * Called to service an incoming event. This interface is implemented by user code to provide the
68 | * action for a given background function. If this method throws any exception (including any
69 | * {@link Error}) then the HTTP response will have a 500 status code.
70 | *
71 | * @param json the payload of the event, as a JSON string.
72 | * @param context the context of the event. This is a set of values that every event has,
73 | * separately from the payload, such as timestamp and event type.
74 | * @throws Exception to produce a 500 status code in the HTTP response.
75 | */
76 | void accept(String json, Context context) throws Exception;
77 | }
78 |
--------------------------------------------------------------------------------
/functions-framework-api/src/main/java/com/google/cloud/functions/TypedFunction.java:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.cloud.functions;
16 |
17 | import java.lang.reflect.Type;
18 |
19 | /**
20 | * Represents a Cloud Function with a strongly typed interface that is activated by an HTTP request.
21 | */
22 | @FunctionalInterface
23 | public interface TypedFunction{@code
17 | * $ functions-framework-conformance-client \
18 | * -cmd="mvn function:run -Drun.functionTarget=com.google.cloud.functions.conformance.BackgroundEventConformanceFunction" \
19 | * -type=legacyevent \
20 | * -buildpacks=false \
21 | * -validate-mapping=false \
22 | * -start-delay=10
23 | * }
24 | */
25 | public class BackgroundEventConformanceFunction implements RawBackgroundFunction {
26 |
27 | private static final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
28 |
29 | @Override
30 | public void accept(String data, Context context) throws Exception {
31 | try (BufferedWriter writer = new BufferedWriter(new FileWriter("function_output.json"))) {
32 | writer.write(serialize(data, context));
33 | }
34 | }
35 |
36 | /** Create a structured JSON representation of the request context and data */
37 | private String serialize(String data, Context context) {
38 | JsonObject contextJson = new JsonObject();
39 | contextJson.addProperty("eventId", context.eventId());
40 | contextJson.addProperty("timestamp", context.timestamp());
41 | contextJson.addProperty("eventType", context.eventType());
42 |
43 | if (context.resource().startsWith("{")) {
44 | JsonElement resource = gson.fromJson(context.resource(), JsonElement.class);
45 | contextJson.add("resource", resource);
46 | } else {
47 | contextJson.addProperty("resource", context.resource());
48 | }
49 |
50 | JsonObject dataJson = gson.fromJson(data, JsonObject.class);
51 |
52 | JsonObject json = new JsonObject();
53 | json.add("data", dataJson);
54 | json.add("context", contextJson);
55 | return gson.toJson(json);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/invoker/conformance/src/main/java/com/google/cloud/functions/conformance/CloudEventsConformanceFunction.java:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.cloud.functions.conformance;
16 |
17 | import static java.nio.charset.StandardCharsets.UTF_8;
18 |
19 | import com.google.cloud.functions.CloudEventsFunction;
20 | import io.cloudevents.CloudEvent;
21 | import io.cloudevents.core.format.EventFormat;
22 | import io.cloudevents.core.provider.EventFormatProvider;
23 | import io.cloudevents.jackson.JsonFormat;
24 | import java.io.BufferedWriter;
25 | import java.io.FileWriter;
26 |
27 | /**
28 | * This class is used by the Functions Framework Conformance Tools to validate the framework's Cloud
29 | * Events API. It can be run with the following command:
30 | *
31 | * {@code
32 | * $ functions-framework-conformance-client \
33 | * -cmd="mvn function:run -Drun.functionTarget=com.google.cloud.functions.conformance.CloudEventsConformanceFunction" \
34 | * -type=cloudevent \
35 | * -buildpacks=false \
36 | * -validate-mapping=false \
37 | * -start-delay=5
38 | * }
39 | */
40 | public class CloudEventsConformanceFunction implements CloudEventsFunction {
41 |
42 | @Override
43 | public void accept(CloudEvent event) throws Exception {
44 | try (BufferedWriter writer = new BufferedWriter(new FileWriter("function_output.json"))) {
45 | EventFormat format = EventFormatProvider.getInstance().resolveFormat(JsonFormat.CONTENT_TYPE);
46 | writer.write(new String(format.serialize(event), UTF_8));
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/invoker/conformance/src/main/java/com/google/cloud/functions/conformance/ConcurrentHttpConformanceFunction.java:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.cloud.functions.conformance;
16 |
17 | import com.google.cloud.functions.HttpFunction;
18 | import com.google.cloud.functions.HttpRequest;
19 | import com.google.cloud.functions.HttpResponse;
20 |
21 | /**
22 | * This class is used by the Functions Framework Conformance Tools to validate concurrency for HTTP
23 | * functions. It can be run with the following command:
24 | *
25 | * {@code
26 | * $ functions-framework-conformance-client \
27 | * -cmd="mvn function:run -Drun.functionTarget=com.google.cloud.functions.conformance.ConcurrentHttpConformanceFunction" \
28 | * -type=http \
29 | * -buildpacks=false \
30 | * -validate-mapping=false \
31 | * -start-delay=5 \
32 | * -validate-concurrency=true
33 | * }
34 | */
35 | public class ConcurrentHttpConformanceFunction implements HttpFunction {
36 |
37 | @Override
38 | public void service(HttpRequest request, HttpResponse response) throws InterruptedException {
39 | Thread.sleep(1000);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/invoker/conformance/src/main/java/com/google/cloud/functions/conformance/HttpConformanceFunction.java:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.cloud.functions.conformance;
16 |
17 | import com.google.cloud.functions.HttpFunction;
18 | import com.google.cloud.functions.HttpRequest;
19 | import com.google.cloud.functions.HttpResponse;
20 | import java.io.BufferedReader;
21 | import java.io.BufferedWriter;
22 | import java.io.FileWriter;
23 | import java.io.IOException;
24 |
25 | /**
26 | * This class is used by the Functions Framework Conformance Tools to validate the framework's HTTP
27 | * API. It can be run with the following command:
28 | *
29 | * {@code
30 | * $ functions-framework-conformance-client \
31 | * -cmd="mvn function:run -Drun.functionTarget=com.google.cloud.functions.conformance.HttpConformanceFunction" \
32 | * -type=http \
33 | * -buildpacks=false \
34 | * -validate-mapping=false \
35 | * -start-delay=5
36 | * }
37 | */
38 | public class HttpConformanceFunction implements HttpFunction {
39 |
40 | @Override
41 | public void service(HttpRequest request, HttpResponse response) throws IOException {
42 | try (BufferedReader reader = request.getReader();
43 | BufferedWriter writer = new BufferedWriter(new FileWriter("function_output.json"))) {
44 | int c;
45 | while ((c = reader.read()) != -1) {
46 | writer.write(c);
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/invoker/conformance/src/main/java/com/google/cloud/functions/conformance/TypedConformanceFunction.java:
--------------------------------------------------------------------------------
1 | package com.google.cloud.functions.conformance;
2 |
3 | import com.google.cloud.functions.TypedFunction;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | public class TypedConformanceFunction
7 | implements TypedFunction