diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index e65ee4533..000000000 --- a/.editorconfig +++ /dev/null @@ -1,14 +0,0 @@ -# EditorConfig is awesome: https://EditorConfig.org - -# top-most EditorConfig file -root = true - -[*] -indent_style = tab -indent_size = 4 -end_of_line = lf -insert_final_newline = true - -[*.yml] -indent_style = space -indent_size = 2 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index d3204a48c..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: waiting-for-triage -assignees: '' - ---- - -**Describe the bug** -Please provide details of the problem, including the version of Spring Cloud that you -are using. - -**Sample** -If possible, please provide a test case or sample application that reproduces -the problem. This makes it much easier for us to diagnose the problem and to verify that -we have fixed it. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index b2e33c5fa..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: waiting-for-triage -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 000000000..f468738cf --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,53 @@ +name: Deploy Docs +run-name: ${{ format('{0} ({1})', github.workflow, github.event.inputs.build-refname || 'all') }} +on: + workflow_dispatch: + inputs: + build-refname: + description: Enter git refname to build (e.g., 5.7.x). + required: false + push: + branches: docs-build +env: + GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }} +permissions: + contents: write +jobs: + build: + if: github.repository_owner == 'spring-cloud' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 5 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + - name: Set up refname build + if: github.event.inputs.build-refname + run: | + git fetch --depth 1 https://github.com/$GITHUB_REPOSITORY ${{ github.event.inputs.build-refname }} + export BUILD_REFNAME=${{ github.event.inputs.build-refname }} + echo "BUILD_REFNAME=$BUILD_REFNAME" >> $GITHUB_ENV + export BUILD_VERSION=$(git cat-file --textconv FETCH_HEAD:pom.xml | python3 -c "import xml.etree.ElementTree as xml; from sys import stdin; print(xml.parse(stdin).getroot().find('{http://maven.apache.org/POM/4.0.0}version').text)") + echo BUILD_VERSION=$BUILD_VERSION >> $GITHUB_ENV + - name: Run Antora + run: | + ./mvnw --no-transfer-progress -B antora -Pdocs + - name: Publish Docs + uses: spring-io/spring-doc-actions/rsync-antora-reference@v0.0.13 + with: + docs-username: ${{ secrets.DOCS_USERNAME }} + docs-host: ${{ secrets.DOCS_HOST }} + docs-ssh-key: ${{ secrets.DOCS_SSH_KEY }} + docs-ssh-host-key: ${{ secrets.DOCS_SSH_HOST_KEY }} + site-path: target/antora/site + - name: Bust Cloudflare Cache + uses: spring-io/spring-doc-actions/bust-cloudflare-antora-cache@v0.0.11 + with: + context-root: spring-cloud-function + cloudflare-zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }} + cloudflare-cache-token: ${{ secrets.CLOUDFLARE_CACHE_TOKEN }} diff --git a/.gitignore b/.gitignore index 51bb91bbc..64ce0fa6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,23 @@ -/application.yml -/application.properties -asciidoctor.css -*~ -.#* -*# target/ -build/ -bin/ -.sts4-cache/ -.attach_pid* -.m2/ -.gradle/ -_site/ -.vscode/ -.classpath -.project .settings/ +.project +.classpath +*.orig .springBeans +.factorypath +.sts4-cache +.ant-targets-build.xml +src/ant/.ant-targets-upload-dist.xml +*.sonar4clipse* .DS_Store -*.sw* *.iml *.ipr *.iws -.idea/ -.factorypath -.checkstyle -coverage-error.log -.apt_generated -credentials.yml -.flattened-pom.xml -pom.xml.versionsBackup +/.idea/ +*.graphml +node +node_modules +build +docs/package.json +package-lock.json +.flattened-pom.xml \ No newline at end of file diff --git a/.mvn/jvm.config b/.mvn/jvm.config index 52b2013a8..0e7dabeff 100644 --- a/.mvn/jvm.config +++ b/.mvn/jvm.config @@ -1 +1 @@ --Xmx1024m -XX:CICompilerCount=1 -noverify -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom \ No newline at end of file +-Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom \ No newline at end of file diff --git a/.mvn/maven.config b/.mvn/maven.config deleted file mode 100644 index 3b8cf46e1..000000000 --- a/.mvn/maven.config +++ /dev/null @@ -1 +0,0 @@ --DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring diff --git a/.mvn/settings.xml b/.mvn/settings.xml deleted file mode 100644 index 2e2841999..000000000 --- a/.mvn/settings.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - sonatype-nexus-staging - ${env.sonatype_username} - ${env.sonatype_password} - - - repo.spring.io - ${env.spring_username} - ${env.spring_password} - - - diff --git a/.springformat b/.springformat deleted file mode 100644 index e69de29bb..000000000 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9e59b8e23..000000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: java -jdk: - - oraclejdk8 -sudo: required -cache: - directories: - - $HOME/.m2 -install: true -before_script: ./mvnw install -q -U -DskipTests=true -Dmaven.test.redirectTestOutputToFile=true -script: ./mvnw install -q -nsu -Dmaven.test.redirectTestOutputToFile=true -dist: trusty diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc deleted file mode 100644 index 8e009c69d..000000000 --- a/CONTRIBUTING.adoc +++ /dev/null @@ -1,3 +0,0 @@ -If you have not previously done so, please fill out and -submit the https://cla.pivotal.io/sign/spring[Contributor License Agreement]. - diff --git a/Guardfile b/Guardfile deleted file mode 100644 index da91d82bb..000000000 --- a/Guardfile +++ /dev/null @@ -1,12 +0,0 @@ -require 'asciidoctor' -require 'erb' -require './src/main/ruby/readme.rb' - -options = {:mkdirs => true, :safe => :unsafe, :attributes => ['linkcss', 'allow-uri-read']} - -guard 'shell' do - watch(/^src\/[A-Z-a-z][^#]*\.adoc$/) {|m| - SpringCloud::Build.render_file('src/main/asciidoc/README.adoc', :to_file => './README.adoc') - Asciidoctor.render_file('src/main/asciidoc/spring-cloud-cli.adoc', options.merge(:to_dir => 'target/generated-docs')) - } -end diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 62589edd1..000000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.adoc b/README.adoc index 139cdf601..e15e4f9f2 100644 --- a/README.adoc +++ b/README.adoc @@ -1,411 +1,23 @@ -//// -DO NOT EDIT THIS FILE. IT WAS GENERATED. -Manual changes to this file will be lost when it is generated again. -Edit the files in the src/main/asciidoc/ directory instead. -//// += Spring Cloud Function Docs Build +You're currently viewing the Antora playbook branch. +The playbook branch hosts the docs build that is used to build and publish the production docs site. -:branch: master +The Spring Cloud Function reference docs are built using https://antora.org[Antora]. +This README covers how to build the docs in a software branch as well as how to build the production docs site locally. -image::https://travis-ci.org/spring-cloud/spring-cloud-function.svg?branch={branch}[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-function] +== Building the Site -== Introduction - -Spring Cloud Function is a project with the following high-level goals: - -* Promote the implementation of business logic via functions. -* Decouple the development lifecycle of business logic from any specific runtime target so that the same code can run as a web endpoint, a stream processor, or a task. -* Support a uniform programming model across serverless providers, as well as the ability to run standalone (locally or in a PaaS). -* Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers. - -It abstracts away all of the transport details and -infrastructure, allowing the developer to keep all the familiar tools -and processes, and focus firmly on business logic. - -Here's a complete, executable, testable Spring Boot application -(implementing a simple string manipulation): - -[source,java] ----- -@SpringBootApplication -public class Application { - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} ----- - -It's just a Spring Boot application, so it can be built, run and -tested, locally and in a CI build, the same way as any other Spring -Boot application. The `Function` is from `java.util` and `Flux` is a -https://www.reactive-streams.org/[Reactive Streams] `Publisher` from -https://projectreactor.io/[Project Reactor]. The function can be -accessed over HTTP or messaging. - -Spring Cloud Function has the following features: - -* _Choice of programming styles - reactive, imperative or hybrid._ -* _Function composition and adaptation (e.g., composing imperative functions with reactive)._ -* _Support for reactive function with multiple inputs and outputs allowing merging, joining and other complex streaming operation to be handled by functions._ -* _Transparent type conversion of inputs and outputs._ -* _Packaging functions for deployments, specific to the target platform (e.g., Project Riff, AWS Lambda and more)_ -* _Adapters to expose function to the outside world as HTTP endpoints etc._ -* _Deploying a JAR file containing such an application context with an isolated classloader, so that you can pack them together in a single JVM._ -* _Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp[Google Cloud Functions], and possibly other "serverless" service providers._ - -== Getting Started - -Build from the command line (and "install" the samples): - ----- -$ ./mvnw clean install ----- - -(If you like to YOLO add `-DskipTests`.) - -Run one of the samples, e.g. - ----- -$ java -jar spring-cloud-function-samples/function-sample/target/*.jar ----- - -This runs the app and exposes its functions over HTTP, so you can -convert a string to uppercase, like this: - ----- -$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d Hello -HELLO ----- - -You can convert multiple strings (a `Flux`) by separating them -with new lines +You can build the entire site by invoking the following on the docs-build branch and then viewing the site at `target/site/index.html` +[source,bash] ---- -$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d 'Hello -> World' -HELLOWORLD +./mvnw antora ---- -(You can use `^Q^J` in a terminal to insert a new line in a literal -string like that.) - -== Building - -:jdkversion: 17 - -=== Basic Compile and Test - -To build the source you will need to install JDK {jdkversion}. - -Spring Cloud uses Maven for most build-related activities, and you -should be able to get off the ground quite quickly by cloning the -project you are interested in and typing +== Building a Specific Branch +[source,bash] ---- -$ ./mvnw install +./mvnw antora ---- - -NOTE: You can also install Maven (>=3.3.3) yourself and run the `mvn` command -in place of `./mvnw` in the examples below. If you do that you also -might need to add `-P spring` if your local Maven settings do not -contain repository declarations for spring pre-release artifacts. - -NOTE: Be aware that you might need to increase the amount of memory -available to Maven by setting a `MAVEN_OPTS` environment variable with -a value like `-Xmx512m -XX:MaxPermSize=128m`. We try to cover this in -the `.mvn` configuration, so if you find you have to do it to make a -build succeed, please raise a ticket to get the settings added to -source control. - -The projects that require middleware (i.e. Redis) for testing generally -require that a local instance of [Docker](https://www.docker.com/get-started) is installed and running. - - -=== Documentation - -The spring-cloud-build module has a "docs" profile, and if you switch -that on it will try to build asciidoc sources from -`src/main/asciidoc`. As part of that process it will look for a -`README.adoc` and process it by loading all the includes, but not -parsing or rendering it, just copying it to `${main.basedir}` -(defaults to `${basedir}`, i.e. the root of the project). If there are -any changes in the README it will then show up after a Maven build as -a modified file in the correct place. Just commit it and push the change. - -=== Working with the code -If you don't have an IDE preference we would recommend that you use -https://www.springsource.com/developer/sts[Spring Tools Suite] or -https://eclipse.org[Eclipse] when working with the code. We use the -https://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools -should also work without issue as long as they use Maven 3.3.3 or better. - -==== Activate the Spring Maven profile -Spring Cloud projects require the 'spring' Maven profile to be activated to resolve -the spring milestone and snapshot repositories. Use your preferred IDE to set this -profile to be active, or you may experience build errors. - -==== Importing into eclipse with m2eclipse -We recommend the https://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with -eclipse. If you don't already have m2eclipse installed it is available from the "eclipse -marketplace". - -NOTE: Older versions of m2e do not support Maven 3.3, so once the -projects are imported into Eclipse you will also need to tell -m2eclipse to use the right profile for the projects. If you -see many different errors related to the POMs in the projects, check -that you have an up to date installation. If you can't upgrade m2e, -add the "spring" profile to your `settings.xml`. Alternatively you can -copy the repository settings from the "spring" profile of the parent -pom into your `settings.xml`. - -==== Importing into eclipse without m2eclipse -If you prefer not to use m2eclipse you can generate eclipse project metadata using the -following command: - -[indent=0] ----- - $ ./mvnw eclipse:eclipse ----- - -The generated eclipse projects can be imported by selecting `import existing projects` -from the `file` menu. - - -== Contributing - -:spring-cloud-build-branch: master - -Spring Cloud is released under the non-restrictive Apache 2.0 license, -and follows a very standard Github development process, using Github -tracker for issues and merging pull requests into master. If you want -to contribute even something trivial please do not hesitate, but -follow the guidelines below. - -=== Sign the Contributor License Agreement -Before we accept a non-trivial patch or pull request we will need you to sign the -https://cla.pivotal.io/sign/spring[Contributor License Agreement]. -Signing the contributor's agreement does not grant anyone commit rights to the main -repository, but it does mean that we can accept your contributions, and you will get an -author credit if we do. Active contributors might be asked to join the core team, and -given the ability to merge pull requests. - -=== Code of Conduct -This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc[code of -conduct]. By participating, you are expected to uphold this code. Please report -unacceptable behavior to spring-code-of-conduct@pivotal.io. - -=== Code Conventions and Housekeeping -None of these is essential for a pull request, but they will all help. They can also be -added after the original pull request but before a merge. - -* Use the Spring Framework code format conventions. If you use Eclipse - you can import formatter settings using the - `eclipse-code-formatter.xml` file from the - https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml[Spring - Cloud Build] project. If using IntelliJ, you can use the - https://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter - Plugin] to import the same file. -* Make sure all new `.java` files to have a simple Javadoc class comment with at least an - `@author` tag identifying you, and preferably at least a paragraph on what the class is - for. -* Add the ASF license header comment to all new `.java` files (copy from existing files - in the project) -* Add yourself as an `@author` to the .java files that you modify substantially (more - than cosmetic changes). -* Add some Javadocs and, if you change the namespace, some XSD doc elements. -* A few unit tests would help a lot as well -- someone has to do it. -* If no-one else is using your branch, please rebase it against the current master (or - other target branch in the main project). -* When writing a commit message please follow https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions], - if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit - message (where XXXX is the issue number). - -=== Checkstyle - -Spring Cloud Build comes with a set of checkstyle rules. You can find them in the `spring-cloud-build-tools` module. The most notable files under the module are: - -.spring-cloud-build-tools/ ----- -└── src -    ├── checkstyle -    │   └── checkstyle-suppressions.xml <3> -    └── main -    └── resources -    ├── checkstyle-header.txt <2> -    └── checkstyle.xml <1> ----- -<1> Default Checkstyle rules -<2> File header setup -<3> Default suppression rules - -==== Checkstyle configuration - -Checkstyle rules are *disabled by default*. To add checkstyle to your project just define the following properties and plugins. - -.pom.xml ----- - -true <1> - true - <2> - true - <3> - - - - - <4> - io.spring.javaformat - spring-javaformat-maven-plugin - - <5> - org.apache.maven.plugins - maven-checkstyle-plugin - - - - - - <5> - org.apache.maven.plugins - maven-checkstyle-plugin - - - - ----- -<1> Fails the build upon Checkstyle errors -<2> Fails the build upon Checkstyle violations -<3> Checkstyle analyzes also the test sources -<4> Add the Spring Java Format plugin that will reformat your code to pass most of the Checkstyle formatting rules -<5> Add checkstyle plugin to your build and reporting phases - -If you need to suppress some rules (e.g. line length needs to be longer), then it's enough for you to define a file under `${project.root}/src/checkstyle/checkstyle-suppressions.xml` with your suppressions. Example: - -.projectRoot/src/checkstyle/checkstyle-suppresions.xml ----- - - - - - - ----- - -It's advisable to copy the `${spring-cloud-build.rootFolder}/.editorconfig` and `${spring-cloud-build.rootFolder}/.springformat` to your project. That way, some default formatting rules will be applied. You can do so by running this script: - -```bash -$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/.editorconfig -o .editorconfig -$ touch .springformat -``` - -=== IDE setup - -==== Intellij IDEA - -In order to setup Intellij you should import our coding conventions, inspection profiles and set up the checkstyle plugin. -The following files can be found in the https://github.com/spring-cloud/spring-cloud-build/tree/master/spring-cloud-build-tools[Spring Cloud Build] project. - -.spring-cloud-build-tools/ ----- -└── src -    ├── checkstyle -    │   └── checkstyle-suppressions.xml <3> -    └── main -    └── resources -    ├── checkstyle-header.txt <2> -    ├── checkstyle.xml <1> -    └── intellij -       ├── Intellij_Project_Defaults.xml <4> -       └── Intellij_Spring_Boot_Java_Conventions.xml <5> ----- -<1> Default Checkstyle rules -<2> File header setup -<3> Default suppression rules -<4> Project defaults for Intellij that apply most of Checkstyle rules -<5> Project style conventions for Intellij that apply most of Checkstyle rules - -.Code style - -image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-code-style.png[Code style] - -Go to `File` -> `Settings` -> `Editor` -> `Code style`. There click on the icon next to the `Scheme` section. There, click on the `Import Scheme` value and pick the `Intellij IDEA code style XML` option. Import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Spring_Boot_Java_Conventions.xml` file. - -.Inspection profiles - -image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-inspections.png[Code style] - -Go to `File` -> `Settings` -> `Editor` -> `Inspections`. There click on the icon next to the `Profile` section. There, click on the `Import Profile` and import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Project_Defaults.xml` file. - -.Checkstyle - -To have Intellij work with Checkstyle, you have to install the `Checkstyle` plugin. It's advisable to also install the `Assertions2Assertj` to automatically convert the JUnit assertions - -image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-checkstyle.png[Checkstyle] - -Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on the `+` icon in the `Configuration file` section. There, you'll have to define where the checkstyle rules should be picked from. In the image above, we've picked the rules from the cloned Spring Cloud Build repository. However, you can point to the Spring Cloud Build's GitHub repository (e.g. for the `checkstyle.xml` : `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle.xml`). We need to provide the following variables: - -- `checkstyle.header.file` - please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` URL. -- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL. -- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`. - -IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources. - -=== Duplicate Finder - -Spring Cloud Build brings along the `basepom:duplicate-finder-maven-plugin`, that enables flagging duplicate and conflicting classes and resources on the java classpath. - -==== Duplicate Finder configuration - -Duplicate finder is *enabled by default* and will run in the `verify` phase of your Maven build, but it will only take effect in your project if you add the `duplicate-finder-maven-plugin` to the `build` section of the projecst's `pom.xml`. - -.pom.xml -[source,xml] ----- - - - - org.basepom.maven - duplicate-finder-maven-plugin - - - ----- - -For other properties, we have set defaults as listed in the https://github.com/basepom/duplicate-finder-maven-plugin/wiki[plugin documentation]. - -You can easily override them but setting the value of the selected property prefixed with `duplicate-finder-maven-plugin`. For example, set `duplicate-finder-maven-plugin.skip` to `true` in order to skip duplicates check in your build. - -If you need to add `ignoredClassPatterns` or `ignoredResourcePatterns` to your setup, make sure to add them in the plugin configuration section of your project: - -[source,xml] ----- - - - - org.basepom.maven - duplicate-finder-maven-plugin - - - org.joda.time.base.BaseDateTime - .*module-info - - - changelog.txt - - - - - - - ----- - diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 36ef17ca6..000000000 --- a/SECURITY.md +++ /dev/null @@ -1,8 +0,0 @@ -**Describe the bug** -Please provide details of the problem, including the version of Spring Cloud that you -are using. - -**Sample** -If possible, please provide a test case or sample application that reproduces -the problem. This makes it much easier for us to diagnose the problem and to verify that -we have fixed it. diff --git a/antora-playbook.yml b/antora-playbook.yml new file mode 100644 index 000000000..863595b87 --- /dev/null +++ b/antora-playbook.yml @@ -0,0 +1,39 @@ +antora: + extensions: + - require: '@springio/antora-extensions' + root_component_name: 'cloud-function' +site: + title: Spring Cloud Function + url: https://docs.spring.io/spring-cloud-function/reference + robots: allow +git: + ensure_git_suffix: false +content: + sources: + - url: https://github.com/spring-cloud/spring-cloud-function + # Refname matching: + # https://docs.antora.org/antora/latest/playbook/content-refname-matching/ + branches: [ main, 4.3.x, 4.2.x ] + tags: [ 'v{4..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!v4.1.0-M1', '!v4.0.*' ] + start_path: docs +asciidoc: + attributes: + page-stackoverflow-url: https://stackoverflow.com/tags/spring-cloud + page-pagination: '' + hide-uri-scheme: '@' + tabs-sync-option: '@' + extensions: + - '@asciidoctor/tabs' + - '@springio/asciidoctor-extensions' +urls: + latest_version_segment_strategy: redirect:to + latest_version_segment: '' + redirect_facility: httpd +ui: + bundle: + url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip + snapshot: true +runtime: + log: + failure_level: warn + format: pretty diff --git a/docs/pom.xml b/docs/pom.xml deleted file mode 100644 index 35f1d5e4a..000000000 --- a/docs/pom.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - 4.0.0 - spring-cloud-function-docs - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - pom - Spring Cloud Function Docs - Spring Cloud Function Docs - - spring-cloud-function - ${basedir}/.. - 3.4 - deploy - - - - docs - - - - pl.project13.maven - git-commit-id-plugin - - - org.apache.maven.plugins - maven-dependency-plugin - - - org.apache.maven.plugins - maven-resources-plugin - - - org.codehaus.mojo - exec-maven-plugin - - - org.asciidoctor - asciidoctor-maven-plugin - - - ${project.version} - ${spring-boot.version} - - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - org.codehaus.mojo - build-helper-maven-plugin - - - maven-deploy-plugin - - - - - - diff --git a/docs/src/main/asciidoc/README.adoc b/docs/src/main/asciidoc/README.adoc deleted file mode 100644 index 6d0f4cf8d..000000000 --- a/docs/src/main/asciidoc/README.adoc +++ /dev/null @@ -1,19 +0,0 @@ -:branch: master - -image::https://travis-ci.org/spring-cloud/spring-cloud-function.svg?branch={branch}[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-function] - -== Introduction - -include::_intro.adoc[] - -== Getting Started - -include::getting-started.adoc[] - -== Building - -include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building.adoc[] - -== Contributing - -include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing.adoc[] diff --git a/docs/src/main/asciidoc/_intro.adoc b/docs/src/main/asciidoc/_intro.adoc deleted file mode 100644 index 76c97c73c..000000000 --- a/docs/src/main/asciidoc/_intro.adoc +++ /dev/null @@ -1,47 +0,0 @@ -Spring Cloud Function is a project with the following high-level goals: - -* Promote the implementation of business logic via functions. -* Decouple the development lifecycle of business logic from any specific runtime target so that the same code can run as a web endpoint, a stream processor, or a task. -* Support a uniform programming model across serverless providers, as well as the ability to run standalone (locally or in a PaaS). -* Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers. - -It abstracts away all of the transport details and -infrastructure, allowing the developer to keep all the familiar tools -and processes, and focus firmly on business logic. - -Here's a complete, executable, testable Spring Boot application -(implementing a simple string manipulation): - -[source,java] ----- -@SpringBootApplication -public class Application { - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} ----- - -It's just a Spring Boot application, so it can be built, run and -tested, locally and in a CI build, the same way as any other Spring -Boot application. The `Function` is from `java.util` and `Flux` is a -https://www.reactive-streams.org/[Reactive Streams] `Publisher` from -https://projectreactor.io/[Project Reactor]. The function can be -accessed over HTTP or messaging. - -Spring Cloud Function has the following features: - -* _Choice of programming styles - reactive, imperative or hybrid._ -* _Function composition and adaptation (e.g., composing imperative functions with reactive)._ -* _Support for reactive function with multiple inputs and outputs allowing merging, joining and other complex streaming operation to be handled by functions._ -* _Transparent type conversion of inputs and outputs._ -* _Packaging functions for deployments, specific to the target platform (e.g., Project Riff, AWS Lambda and more)_ -* _Adapters to expose function to the outside world as HTTP endpoints etc._ -* _Deploying a JAR file containing such an application context with an isolated classloader, so that you can pack them together in a single JVM._ -* _Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp[Google Cloud Functions], and possibly other "serverless" service providers._ diff --git a/docs/src/main/asciidoc/adapters/aws-intro.adoc b/docs/src/main/asciidoc/adapters/aws-intro.adoc deleted file mode 100644 index 8bd82bb2f..000000000 --- a/docs/src/main/asciidoc/adapters/aws-intro.adoc +++ /dev/null @@ -1,318 +0,0 @@ -:branch: master - -=== AWS Lambda - -The https://aws.amazon.com/[AWS] adapter takes a Spring Cloud Function app and converts it to a form that can run in AWS Lambda. - -The details of how to get stared with AWS Lambda is out of scope of this document, so the expectation is that user has some familiarity with -AWS and AWS Lambda and wants to learn what additional value spring provides. - -==== Getting Started - -One of the goals of Spring Cloud Function framework is to provide necessary infrastructure elements to enable a _simple function application_ -to interact in a certain way in a particular environment. -A simple function application (in context or Spring) is an application that contains beans of type Supplier, Function or Consumer. -So, with AWS it means that a simple function bean should somehow be recognised and executed in AWS Lambda environment. - -Let’s look at the example: - -[source, java] ----- -@SpringBootApplication -public class FunctionConfiguration { - - public static void main(String[] args) { - SpringApplication.run(FunctionConfiguration.class, args); - } - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } -} ----- - -It shows a complete Spring Boot application with a function bean defined in it. What’s interesting is that on the surface this is just -another boot app, but in the context of AWS Adapter it is also a perfectly valid AWS Lambda application. No other code or configuration -is required. All you need to do is package it and deploy it, so let’s look how we can do that. - -To make things simpler we’ve provided a sample project ready to be built and deployed and you can access it -https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-aws[here]. - -You simply execute `./mvnw clean package` to generate JAR file. All the necessary maven plugins have already been setup to generate -appropriate AWS deployable JAR file. (You can read more details about JAR layout in <>). - -Then you have to upload the JAR file (via AWS dashboard or AWS CLI) to AWS. - -When ask about _handler_ you specify `org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest` which is a generic request handler. - -image::{github-raw}/docs/src/main/asciidoc/images/AWS-deploy.png[width=800,scaledwidth="75%",align="center"] - -That is all. Save and execute the function with some sample data which for this function is expected to be a -String which function will uppercase and return back. - -While `org.springframework.cloud.function.adapter.aws.FunctionInvoker` is a general purpose AWS's `RequestHandler` implementation aimed at completely -isolating you from the specifics of AWS Lambda API, for some cases you may want to specify which specific AWS's `RequestHandler` you want -to use. The next section will explain you how you can accomplish just that. - - -==== AWS Request Handlers - -The adapter has a couple of generic request handlers that you can use. The most generic is (and the one we used in the Getting Started section) -is `org.springframework.cloud.function.adapter.aws.FunctionInvoker` which is the implementation of AWS's `RequestStreamHandler`. -User doesn't need to do anything other then specify it as 'handler' on AWS dashboard when deploying function. -It will handle most of the case including Kinesis, streaming etc. . - - -If your app has more than one `@Bean` of type `Function` etc. then you can choose the one to use by configuring `spring.cloud.function.definition` -property or environment variable. The functions are extracted from the Spring Cloud `FunctionCatalog`. In the event you don't specify `spring.cloud.function.definition` -the framework will attempt to find a default following the search order where it searches first for `Function` then `Consumer` and finally `Supplier`). - - -==== AWS Function Routing - -One of the core features of Spring Cloud Function is https://docs.spring.io/spring-cloud-function/docs/{project-version}/reference/html/spring-cloud-function.html#_function_routing_and_filtering[routing] -- an ability to have one special function to delegate to other functions based on the user provided routing instructions. - -In AWS Lambda environment this feature provides one additional benefit, as it allows you to bind a single function (Routing Function) -as AWS Lambda and thus a single HTTP endpoint for API Gateway. So in the end you only manage one function and one endpoint, while benefiting -from many function that can be part of your application. - -More details are available in the provided https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-aws-routing[sample], -yet few general things worth mentioning. - -Routing capabilities will be enabled by default whenever there is more then one function in your application as `org.springframework.cloud.function.adapter.aws.FunctionInvoker` -can not determine which function to bind as AWS Lambda, so it defaults to `RoutingFunction`. -This means that all you need to do is provide routing instructions which you can do https://docs.spring.io/spring-cloud-function/docs/{project-version}/reference/html/spring-cloud-function.html#_function_routing_and_filtering[using several mechanisms] -(see https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-aws-routing[sample] for more details). - -Also, note that since AWS does not allow dots `.` and/or hyphens`-` in the name of the environment variable, you can benefit from boot support and simply substitute -dots with underscores and hyphens with camel case. So for example `spring.cloud.function.definition` becomes `spring_cloud_function_definition` -and `spring.cloud.function.routing-expression` becomes `spring_cloud_function_routingExpression`. - -===== AWS Function Routing with Custom Runtime - -When using <> Function Routing works the same way. All you need is to specify `functionRouter` as AWS Handler the same way you would use the name of the function as handler. - -==== Notes on JAR Layout - -You don't need the Spring Cloud Function Web or Stream adapter at runtime in Lambda, so you might -need to exclude those before you create the JAR you send to AWS. A Lambda application has to be -shaded, but a Spring Boot standalone application does not, so you can run the same app using 2 -separate jars (as per the sample). The sample app creates 2 jar files, one with an `aws` -classifier for deploying in Lambda, and one [[thin-jar,thin jar]] executable (thin) jar that includes `spring-cloud-function-web` -at runtime. Spring Cloud Function will try and locate a "main class" for you from the JAR file -manifest, using the `Start-Class` attribute (which will be added for you by the Spring Boot -tooling if you use the starter parent). If there is no `Start-Class` in your manifest you can -use an environment variable or system property `MAIN_CLASS` when you deploy the function to AWS. - -If you are not using the functional bean definitions but relying on Spring Boot's auto-configuration, -and are not depending on `spring-boot-starter-parent`, -then additional transformers must be configured as part of the maven-shade-plugin execution. - -[[shade-plugin-setup]] -[source, xml] ----- - - org.apache.maven.plugins - maven-shade-plugin - - - org.springframework.boot - spring-boot-maven-plugin - 2.7.4 - - - - - - shade - - - false - true - aws - - - META-INF/spring.handlers - - - META-INF/spring.factories - - - META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports - - - META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports - - - META-INF/spring.schemas - - - META-INF/spring.components - - - - - - ----- - -==== Build file setup - -In order to run Spring Cloud Function applications on AWS Lambda, you can leverage Maven or Gradle - plugins offered by the cloud platform provider. - - -===== Maven - -In order to use the adapter plugin for Maven, add the plugin dependency to your `pom.xml` -file: - -[source,xml] ----- - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - ----- - -As pointed out in the <>, you will need a shaded jar in order to upload it -to AWS Lambda. You can use the https://maven.apache.org/plugins/maven-shade-plugin/[Maven Shade Plugin] for that. -The example of the <> can be found above. - -You can use the Spring Boot Maven Plugin to generate the <>. -[source,xml] ----- - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - ----- - -You can find the entire sample `pom.xml` file for deploying Spring Cloud Function -applications to AWS Lambda with Maven https://github.com/spring-cloud/spring-cloud-function/blob/{branch}/spring-cloud-function-samples/function-sample-aws/pom.xml[here]. - -===== Gradle - -In order to use the adapter plugin for Gradle, add the dependency to your `build.gradle` file: - -[source,groovy] ----- - -dependencies { - compile("org.springframework.cloud:spring-cloud-function-adapter-aws:${version}") -} ----- - -As pointed out in <>, you will need a shaded jar in order to upload it -to AWS Lambda. You can use the https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow/[Gradle Shadow Plugin] for that: - -[source,groovy] ----- -buildscript { - dependencies { - classpath "com.github.jengelman.gradle.plugins:shadow:${shadowPluginVersion}" - } -} -apply plugin: 'com.github.johnrengelman.shadow' - -assemble.dependsOn = [shadowJar] - -import com.github.jengelman.gradle.plugins.shadow.transformers.* - -shadowJar { - classifier = 'aws' - dependencies { - exclude( - dependency("org.springframework.cloud:spring-cloud-function-web:${springCloudFunctionVersion}")) - } - // Required for Spring - mergeServiceFiles() - append 'META-INF/spring.handlers' - append 'META-INF/spring.schemas' - append 'META-INF/spring.tooling' - append 'META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports' - append 'META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports' - transform(PropertiesFileTransformer) { - paths = ['META-INF/spring.factories'] - mergeStrategy = "append" - } -} - ----- - -You can use the Spring Boot Gradle Plugin and Spring Boot Thin Gradle Plugin to generate -the <>. - -[source,groovy] ----- -buildscript { - dependencies { - classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}") - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - } -} -apply plugin: 'org.springframework.boot' -apply plugin: 'org.springframework.boot.experimental.thin-launcher' -assemble.dependsOn = [thinJar] ----- - -You can find the entire sample `build.gradle` file for deploying Spring Cloud Function -applications to AWS Lambda with Gradle https://github.com/spring-cloud/spring-cloud-function/blob/{branch}/spring-cloud-function-samples/function-sample-aws/build.gradle[here]. - -==== Upload - -Build the sample under `spring-cloud-function-samples/function-sample-aws` and upload the `-aws` jar file to Lambda. The handler can be `example.Handler` or `org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler` (FQN of the class, _not_ a method reference, although Lambda does accept method references). - ----- -./mvnw -U clean package ----- - -Using the AWS command line tools it looks like this: - ----- -aws lambda create-function --function-name Uppercase --role arn:aws:iam::[USERID]:role/service-role/[ROLE] --zip-file fileb://function-sample-aws/target/function-sample-aws-2.0.0.BUILD-SNAPSHOT-aws.jar --handler org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler --description "Spring Cloud Function Adapter Example" --runtime java8 --region us-east-1 --timeout 30 --memory-size 1024 --publish ----- - -The input type for the function in the AWS sample is a Foo with a single property called "value". So you would need this to test it: - ----- -{ - "value": "test" -} ----- - -NOTE: The AWS sample app is written in the "functional" style (as an `ApplicationContextInitializer`). This is much faster on startup in Lambda than the traditional `@Bean` style, so if you don't need `@Beans` (or `@EnableAutoConfiguration`) it's a good choice. Warm starts are not affected. - - -==== Type Conversion - -Spring Cloud Function will attempt to transparently handle type conversion between the raw -input stream and types declared by your function. - -For example, if your function signature is as such `Function` we will attempt to convert -incoming stream event to an instance of `Foo`. - -In the event type is not known or can not be determined (e.g., `Function`) we will attempt to -convert an incoming stream event to a generic `Map`. - -====== Raw Input - -There are times when you may want to have access to a raw input. In this case all you need is to declare your -function signature to accept `InputStream`. For example, `Function`. In this case -we will not attempt any conversion and will pass the raw input directly to a function. - - - - - diff --git a/docs/src/main/asciidoc/adapters/aws.adoc b/docs/src/main/asciidoc/adapters/aws.adoc deleted file mode 100644 index 0912ded38..000000000 --- a/docs/src/main/asciidoc/adapters/aws.adoc +++ /dev/null @@ -1,95 +0,0 @@ -*{project-version}* - - -The https://aws.amazon.com/[AWS] adapter takes a Spring Cloud Function app and converts it to a form that can run in AWS Lambda. - -== Introduction - -include::adapters/aws-intro.adoc[] - -== Functional Bean Definitions - -Your functions will start much quicker if you can use functional bean definitions instead of `@Bean`. To do this make your main class -an `ApplicationContextInitializer` and use the `registerBean()` methods in `GenericApplicationContext` to -create all the beans you need. You function need to be registered as a bean of type `FunctionRegistration` so that the input and -output types can be accessed by the framework. There is an example in github (the AWS sample is written in this style). It would -look something like this: - -```java -@SpringBootConfiguration -public class FuncApplication implements ApplicationContextInitializer { - - public static void main(String[] args) throws Exception { - FunctionalSpringApplication.run(FuncApplication.class, args); - } - - public Function function() { - return value -> new Bar(value.uppercase())); - } - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration>(function()) - .type(FunctionTypeUtils.functionType(Foo.class, Bar.class))); - } - -} -``` - -== AWS Context - -In a typical implementation of AWS Handler user has access to AWS _context_ object. With function approach you can have the same experience if you need it. -Upon each invocation the framework will add `aws-context` message header containing the AWS _context_ instance for that particular invocation. So if you need to access it -you can simply have `Message` as an input parameter to your function and then access `aws-context` from message headers. -For convenience we provide AWSLambdaUtils.AWS_CONTEXT constant. - -== Platform Specific Features - -=== HTTP and API Gateway - -AWS has some platform-specific data types, including batching of messages, which is much more efficient than processing each one individually. To make use of these types you can write a function that depends on those types. Or you can rely on Spring to extract the data from the AWS types and convert it to a Spring `Message`. To do this you tell AWS that the function is of a specific generic handler type (depending on the AWS service) and provide a bean of type `Function,Message>`, where `S` and `T` are your business data types. If there is more than one bean of type `Function` you may also need to configure the Spring Boot property `function.name` to be the name of the target bean (e.g. use `FUNCTION_NAME` as an environment variable). - -The supported AWS services and generic handler types are listed below: - -|=== -| Service | AWS Types | Generic Handler | - -| API Gateway | `APIGatewayProxyRequestEvent`, `APIGatewayProxyResponseEvent` | `org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler` | -| Kinesis | KinesisEvent | org.springframework.cloud.function.adapter.aws.SpringBootKinesisEventHandler | -|=== - - -For example, to deploy behind an API Gateway, use `--handler org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler` in your AWS command line (in via the UI) and define a `@Bean` of type `Function,Message>` where `Foo` and `Bar` are POJO types (the data will be marshalled and unmarshalled by AWS using Jackson). - -== Custom Runtime - -You can also benefit from https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html[AWS Lambda custom runtime] feature of AWS Lambda -and Spring Cloud Function provides all the necessary components to make it easy. - -From the code perspective the application should look no different then any other Spring Cloud Function application. -The only thing you need to do is to provide a `bootstrap` script in the root of your zip/jar that runs the Spring Boot application. -and select "Custom Runtime" when creating a function in AWS. -Here is an example 'bootstrap' file: -```text -#!/bin/sh - -cd ${LAMBDA_TASK_ROOT:-.} - -java -Dspring.main.web-application-type=none -Dspring.jmx.enabled=false \ - -noverify -XX:TieredStopAtLevel=1 -Xss256K -XX:MaxMetaspaceSize=128M \ - -Djava.security.egd=file:/dev/./urandom \ - -cp .:`echo lib/*.jar | tr ' ' :` com.example.LambdaApplication -``` -The `com.example.LambdaApplication` represents your application which contains function beans. - -Set the handler name in AWS to the name of your function. You can use function composition here as well (e.g., `uppecrase|reverse`). -That is pretty much all. Once you upload your zip/jar to AWS your function will run in custom runtime. -We provide a https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-aws-custom-new[sample project] -where you can also see how to configure yoru POM to properly generate the zip file. - -The functional bean definition style works for custom runtimes as well, and is -faster than the `@Bean` style. A custom runtime can start up much quicker even than a functional bean implementation -of a Java lambda - it depends mostly on the number of classes you need to load at runtime. -Spring doesn't do very much here, so you can reduce the cold start time by only using primitive types in your function, for instance, -and not doing any work in custom `@PostConstruct` initializers. diff --git a/docs/src/main/asciidoc/adapters/azure-intro.adoc b/docs/src/main/asciidoc/adapters/azure-intro.adoc deleted file mode 100644 index 59ceca215..000000000 --- a/docs/src/main/asciidoc/adapters/azure-intro.adoc +++ /dev/null @@ -1,533 +0,0 @@ -:branch: master - -== Microsoft Azure Functions -:sectnums: - -https://azure.microsoft.com[Azure] function adapter for deploying `Spring Cloud Function` applications as native Azure Java Functions. - -The `Azure Functions` https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java[programming model] relays, extensively, on Java https://learn.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation?view=azure-java-stable[annotations] for defining the function's handler methods and their input and output types. -At compile time the annotated classes are processed by the provided Azure Maven/Gradle plugins to generate the necessary Azure Function binding files, configurations and package artifacts. -The Azure annotations are just a type-safe way to configure your java function to be recognized as Azure function. - -The https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[spring-cloud-function-adapter-azure] extends the basic programming model to provide Spring and Spring Cloud Function support. -With the adapter you can build your Spring Cloud Function application using dependency injections and then auto-wire the necessary services into your Azure handler methods. - -image::../images/scf-azure-adapter.svg[width=800,scaledwidth="75%",align="center"] - -TIP: For Web-based function applications, you can replace the generic `adapter-azure` with the specialized https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web[spring-cloud-function-adapter-azure-web]. -With the Azure Web Adapter you can deploy any Spring Web application as an Azure, HttpTrigger, function. -This adapter hides the Azure annotations complexity and uses the familiar https://docs.spring.io/spring-boot/docs/current/reference/html/web.html[Spring Web] programming model instead. -For further information follow the <> section below. - -== Azure Adapter - -Provides `Spring` & `Spring Cloud Function` integration for Azure Functions. - -=== Dependencies - -In order to enable the Azure Function integration add the azure adapter dependency to your `pom.xml` or `build.gradle` -files: - -==== -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- - - - org.springframework.cloud - spring-cloud-function-adapter-azure - - ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Gradle ----- -dependencies { - implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure' -} ----- -==== - -NOTE: version `4.0.0+` is required. Having the adapter on the classpath activates the Azure Java Worker integration. - -[[azure.development.guidelines]] -=== Development Guidelines - -Use the `@Component` (or `@Service`) annotation to turn any exiting Azure Function class (e.g. with `@FunctionName` handlers) into a Spring component. -Then you can auto-wire the required dependencies (or the <> for Spring Cloud Function composition) and use those inside the Azure function handlers. - -[source,java] ----- -@Component // <1> -public class MyAzureFunction { - - // Plain Spring bean - not a Spring Cloud Functions! - @Autowired private Function uppercase; // <2> - - // The FunctionCatalog leverages the Spring Cloud Function framework. - @Autowired private FunctionCatalog functionCatalog; // <2> - - @FunctionName("spring") // <3> - public String plainBean( // <4> - @HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - - return this.uppercase.apply(request.getBody().get()); - } - - @FunctionName("scf") // <3> - public String springCloudFunction( // <5> - @HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - - // Use SCF composition. Composed functions are not just spring beans but SCF such. - Function composed = this.functionCatalog.lookup("reverse|uppercase"); // <6> - - return (String) composed.apply(request.getBody().get()); - } -} ----- - -<1> Indicates that the `MyAzureFunction` class is a "component" to be considered by the Spring Framework as a candidate for auto-detection and classpath scanning. -<2> Auto-wire the `uppercase` and `functionCatalog` beans defined in the `HttpTriggerDemoApplication` (below). -<3> The https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#java-function-basics[@FunctionName] annotation identifies the designated Azure function handlers. -When invoked by a trigger (such as `@HttpTrigger`), functions process that trigger, and any other inputs, to produce one or more outputs. -<4> The `plainBean` method handler is mapped to an Azure function that uses of the auto-wired `uppercase` spring bean to compute the result. -It demonstrates how to use "plain" Spring components in your Azure handlers. -<5> The `springCloudFunction` method handler is mapped to another Azure function, that uses the auto-wired `FunctionCatalog` instance to compute the result. -<6> Shows how to leverage the Spring Cloud Function <> composition API. - -TIP: Use the Java annotations included in the https://learn.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation?view=azure-java-stable[com.microsoft.azure.functions.annotation.*] package to bind input and outputs to your methods. - -The implementation of the business logic used inside the Azure handlers looks like a common Spring application: - -[[HttpTriggerDemoApplication]] - -[source,java] ----- -@SpringBootApplication // <1> -public class HttpTriggerDemoApplication { - - public static void main(String[] args) { - SpringApplication.run(HttpTriggerDemoApplication.class, args); - } - - @Bean - public Function uppercase() { // <2> - return payload -> payload.toUpperCase(); - } - - @Bean - public Function reverse() { // <2> - return payload -> new StringBuilder(payload).reverse().toString(); - } -} ----- -<1> The `@SpringBootApplication` annotated class is used as a `Main-Class` as explained in <>. -<2> Functions auto-wired and used in the Azure function handlers. - -==== Function Catalog - -The Spring Cloud Function supports a range of type signatures for user-defined functions, while providing a consistent execution model. -For this it uses the <> to transform all user defined functions into a canonical representation. - -The Azure adapter can auto-wire any Spring component, such as the `uppercase` above. -But those are treated as plain Java class instances, not as a canonical Spring Cloud Functions! - -To leverage Spring Cloud Function and have access to the canonical function representations, you need to auto-wire the `FunctionCatalog` and use it in your handler, like the `functionCatalog` instance the `springCloudFunction()` handler above. - -==== Accessing Azure ExecutionContext - -Some time there is a need to access the target execution context provided by the Azure runtime in the form of `com.microsoft.azure.functions.ExecutionContext`. -For example one of such needs is logging, so it can appear in the Azure console. - -For that purpose the `AzureFunctionUtil.enhanceInputIfNecessary` allow you to add an instance of the `ExecutionContext` as a Message header so you can retrieve it via `executionContext` key. - -[source,java] ----- -@FunctionName("myfunction") -public String execute( - @HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - - Message message = - (Message) AzureFunctionUtil.enhanceInputIfNecessary(request.getBody().get(), context); // <1> - - return this.uppercase.apply(message); -} ----- -<1> Leverages the `AzureFunctionUtil` utility to inline the `context` as message header using the `AzureFunctionUtil.EXECUTION_CONTEXT` header key. - -Now you can retrieve the ExecutionContext from message headers: - -[source,java] ----- -@Bean -public Function, String> uppercase(JsonMapper mapper) { - return message -> { - String value = message.getPayload(); - ExecutionContext context = - (ExecutionContext) message.getHeaders().get(AzureFunctionUtil.EXECUTION_CONTEXT); // <1> - . . . - } -} ----- -<1> Retrieve the ExecutionContext instance from the header. - - -[[azure.configuration]] -=== Configuration - -To run your function applications on Microsoft Azure, you have to provide the necessary configurations, such as `function.json` and `host.json`, and adhere to the compulsory https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#folder-structure[packaging format]. - -Usually the Azure Maven (or Gradle) plugins are used to generate the necessary configurations from the annotated classes and to produce the required package format. - -IMPORTANT: The Azure https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#folder-structure[packaging format] is not compatible with the default Spring Boot packaging (e.g. `uber jar`). -The <> section below explains how to handle this. - -==== Azure Maven/Gradle Plugins - -Azure provides https://github.com/microsoft/azure-maven-plugins/tree/develop/azure-functions-maven-plugin[Maven] and https://github.com/microsoft/azure-gradle-plugins/tree/master/azure-functions-gradle-plugin[Gradle] plugins to process the annotated classes, generate the necessary configurations and produce the expected package layout. -Plugins are used to set the platform, runtime and app-settings properties like this: - -==== -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- - - com.microsoft.azure - azure-functions-maven-plugin - 1.22.0 or higher - - - YOUR-AZURE-FUNCTION-APP-NAME - YOUR-AZURE-FUNCTION-RESOURCE-GROUP - YOUR-AZURE-FUNCTION-APP-REGION - YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME - YOUR-AZURE-FUNCTION-PRICING-TIER - - ${project.basedir}/src/main/resources/host.json - - - linux - 11 - - - - - FUNCTIONS_EXTENSION_VERSION - ~4 - - - - - - package-functions - - package - - - - ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Gradle ----- -plugins { - id "com.microsoft.azure.azurefunctions" version "1.11.0" - // ... -} - -apply plugin: "com.microsoft.azure.azurefunctions" - -azurefunctions { - appName = 'YOUR-AZURE-FUNCTION-APP-NAME' - resourceGroup = 'YOUR-AZURE-FUNCTION-RESOURCE-GROUP' - region = 'YOUR-AZURE-FUNCTION-APP-REGION' - appServicePlanName = 'YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME' - pricingTier = 'YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME' - - runtime { - os = 'linux' - javaVersion = '11' - } - - auth { - type = 'azure_cli' - } - - appSettings { - FUNCTIONS_EXTENSION_VERSION = '~4' - } - // Uncomment to enable local debug - // localDebug = "transport=dt_socket,server=y,suspend=n,address=5005" -} ----- -==== - -More information about the runtime configurations: https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#java-versions[Java Versions], https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#specify-the-deployment-os[Deployment OS]. - -[[disable.spring.boot.plugin]] -==== Disable Spring Boot Plugin - -Expectedly, the Azure Functions run inside the Azure execution runtime, not inside the SpringBoot runtime! -Furthermore, Azure expects a specific packaging format, generated by the Azure Maven/Gradle plugins, that is not compatible with the default Spring Boot packaging. - -You have to either disable the SpringBoot Maven/Gradle plugin or use the https://github.com/dsyer/spring-boot-thin-launcher[Spring Boot Thin Launcher] as shown in this Maven snippet: - -[source,xml] ----- - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - - - ----- - -[[star-class-configuration]] -==== Main-Class Configuration - -Specify the `Main-Class`/`Start-Class` to point to your Spring application entry point, such as the <> class in the example above. - -You can use the Maven `start-class` property or set the `Main-Class` attribute of your `MANIFEST/META-INFO`: - -==== -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- - - YOUR APP MAIN CLASS - ... - ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Gradle ----- -jar { - manifest { - attributes( - "Main-Class": "YOUR-APP-MAIN-CLASS" - ) - } -} ----- -==== - -TIP: Alternatively you can use the `MAIN_CLASS` environment variable to set the class name explicitly. -For local runs, add the `MAIN_CLASS` variable to your `local.settings.json` file and for Azure portal deployment set the variable in the https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings?tabs=portal#get-started-in-the-azure-portal[App Settings]. - - -IMPORTANT: If the `MAIN_CLASS` variable is not set, the Azure adapter lookups the `MANIFEST/META-INFO` attributes from the jars found on the classpath and selects the first `Main-Class:` annotated with either a `@SpringBootApplication` or `@SpringBootConfiguration` annotation. - -==== Metadata Configuration - -You can use a shared https://learn.microsoft.com/en-us/azure/azure-functions/functions-host-json[host.json] file to configure the function app. - -[source,json] ----- -{ - "version": "2.0", - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[4.*, 5.0.0)" - } -} ----- - -The host.json metadata file contains configuration options that affect all functions in a function app instance. - -TIP: If the file is not in the project top folder you need to configure your plugins accordingly (like `hostJson` maven attribute). - -=== Samples - -Here is a list of various Spring Cloud Function Azure Adapter samples you can explore: - -- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-http-trigger[Http Trigger (Maven)] -- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle[Http Trigger (Gradle)] -- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-blob-trigger[Blob Trigger (Maven)] -- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-timer-trigger[Timer Trigger (Maven)] -- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-kafka-trigger[ Kafka Trigger & Output Binding (Maven)]. - -[[azure.web.adapter]] -== Azure Web Adapter - -For, pure, Web-based function applications, you can replace the generic `adapter-azure` with the specialized https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web[spring-cloud-function-adapter-azure-web]. -The Azure Web Adapter can deploy any Spring Web application as a native Azure function, using the HttpTrigger internally. -It hides the Azure annotations complexity and relies on the familiar https://docs.spring.io/spring-boot/docs/current/reference/html/web.html[Spring Web] programming model instead. - -To enable the Azure Web Adapter, add the adapter dependency to your `pom.xml` or `build.gradle` files: - -==== -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- - - - org.springframework.cloud - spring-cloud-function-adapter-azure-web - - ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Gradle ----- -dependencies { - implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure-web' -} ----- -==== - -The same <> and <> instructions apply to the `Azure Web Adapter` as well. - - -=== Samples - -For further information, explore the following, Azure Web Adapter, sample: - -- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-web[ Azure Web Adapter (Maven)]. - -[[azure.usage]] -== Usage - -Common instructions for building and deploying both, `Azure Adapter` and `Azure Web Adapter` type of applications. - -=== Build - -==== -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- -./mvnw -U clean package ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Gradle ----- -./gradlew azureFunctionsPackage ----- -==== - -=== Running locally - -To run locally on top of `Azure Functions`, and to deploy to your live Azure environment, you will need `Azure Functions Core Tools` installed along with the Azure CLI (see https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser#configure-your-local-environment[here]). -For some configuration you would need the https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator[Azurite emulator] as well. - -Then run the sample: - -==== -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- -./mvnw azure-functions:run ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Gradle ----- -./gradlew azureFunctionsRun ----- -==== - -=== Running on Azure - -Make sure you are logged in your Azure account. - ----- -az login ----- - -and deploy - -==== -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- -./mvnw azure-functions:deploy ----- - -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Gradle ----- -./gradlew azureFunctionsDeploy ----- -==== - -=== Debug locally - -Run the function in debug mode. - -==== -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- -./mvnw azure-functions:run -DenableDebug ----- -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] -.Gradle - ----- -// If you want to debug your functions, please add the following line -// to the azurefunctions section of your build.gradle. -azurefunctions { - ... - localDebug = "transport=dt_socket,server=y,suspend=n,address=5005" -} ----- -==== - -Alternatively and the `JAVA_OPTS` value to your `local.settings.json` like this: - -[source,json] ----- -{ - "IsEncrypted": false, - "Values": { - ... - "FUNCTIONS_WORKER_RUNTIME": "java", - "JAVA_OPTS": "-Djava.net.preferIPv4Stack=true -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=127.0.0.1:5005" - } -} ----- - -Here is snippet for a `VSCode` remote debugging configuration: - -[source,json] ----- -{ - "version": "0.2.0", - "configurations": [ - { - "type": "java", - "name": "Attach to Remote Program", - "request": "attach", - "hostName": "localhost", - "port": "5005" - }, - ] -} ----- - -== FunctionInvoker (deprecated) - -WARNING: The legacy `FunctionInvoker` programming model is deprecated and will not be supported going forward. - -For additional documentation and samples about the Function Integration approach follow the https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure/[azure-sample] README and code. - -== Relevant Links - -- https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/getting-started-with-spring-cloud-function-in-azure[Spring Cloud Function in Azure] -- https://spring.io/blog/2023/02/24/spring-cloud-function-for-azure-function[Spring Cloud Function for Azure Function (blog)] -- <> -- https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption[Azure Functions Java developer guide] -- https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference?tabs=blob[Azure Functions developer guide] - -:sectnums!: \ No newline at end of file diff --git a/docs/src/main/asciidoc/adapters/azure.adoc b/docs/src/main/asciidoc/adapters/azure.adoc deleted file mode 100644 index 01dd586d4..000000000 --- a/docs/src/main/asciidoc/adapters/azure.adoc +++ /dev/null @@ -1,3 +0,0 @@ -*{project-version}* - -include::adapters/azure-intro.adoc[] diff --git a/docs/src/main/asciidoc/adapters/gcp-intro.adoc b/docs/src/main/asciidoc/adapters/gcp-intro.adoc deleted file mode 100644 index 1f72b19ed..000000000 --- a/docs/src/main/asciidoc/adapters/gcp-intro.adoc +++ /dev/null @@ -1,295 +0,0 @@ -:branch: master - -=== Google Cloud Functions - -The Google Cloud Functions adapter enables Spring Cloud Function apps to run on the https://cloud.google.com/functions[Google Cloud Functions] serverless platform. -You can either run the function locally using the open source https://github.com/GoogleCloudPlatform/functions-framework-java[Google Functions Framework for Java] or on GCP. - -==== Project Dependencies - -Start by adding the `spring-cloud-function-adapter-gcp` dependency to your project. - -[source, xml] ----- - - - org.springframework.cloud - spring-cloud-function-adapter-gcp - - - ... - ----- - -In addition, add the `spring-boot-maven-plugin` which will build the JAR of the function to deploy. - -NOTE: Notice that we also reference `spring-cloud-function-adapter-gcp` as a dependency of the `spring-boot-maven-plugin`. This is necessary because it modifies the plugin to package your function in the correct JAR format for deployment on Google Cloud Functions. - -[source, xml] ----- - - org.springframework.boot - spring-boot-maven-plugin - - target/deploy - - - - org.springframework.cloud - spring-cloud-function-adapter-gcp - - - ----- - -Finally, add the Maven plugin provided as part of the Google Functions Framework for Java. -This allows you to test your functions locally via `mvn function:run`. - -NOTE: The function target should always be set to `org.springframework.cloud.function.adapter.gcp.GcfJarLauncher`; this is an adapter class which acts as the entry point to your Spring Cloud Function from the Google Cloud Functions platform. - -[source,xml] ----- - - com.google.cloud.functions - function-maven-plugin - 0.9.1 - - org.springframework.cloud.function.adapter.gcp.GcfJarLauncher - 8080 - - ----- - -A full example of a working `pom.xml` can be found in the https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample-gcp-http/pom.xml[Spring Cloud Functions GCP sample]. - -==== HTTP Functions - -Google Cloud Functions supports deploying https://cloud.google.com/functions/docs/writing/http[HTTP Functions], which are functions that are invoked by HTTP request. The sections below describe instructions for deploying a Spring Cloud Function as an HTTP Function. - -===== Getting Started - -Let’s start with a simple Spring Cloud Function example: - -[source, java] ----- -@SpringBootApplication -public class CloudFunctionMain { - - public static void main(String[] args) { - SpringApplication.run(CloudFunctionMain.class, args); - } - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } -} ----- - -Specify your configuration main class in `resources/META-INF/MANIFEST.MF`. - -[source] ----- -Main-Class: com.example.CloudFunctionMain ----- - -Then run the function locally. -This is provided by the Google Cloud Functions `function-maven-plugin` described in the project dependencies section. - ----- -mvn function:run ----- - -Invoke the HTTP function: - ----- -curl http://localhost:8080/ -d "hello" ----- - -===== Deploy to GCP - -Start by packaging your application. - ----- -mvn package ----- - -If you added the custom `spring-boot-maven-plugin` plugin defined above, you should see the resulting JAR in `target/deploy` directory. -This JAR is correctly formatted for deployment to Google Cloud Functions. - -Next, make sure that you have the https://cloud.google.com/sdk/install[Cloud SDK CLI] installed. - -From the project base directory run the following command to deploy. - ----- -gcloud functions deploy function-sample-gcp-http \ ---entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \ ---runtime java11 \ ---trigger-http \ ---source target/deploy \ ---memory 512MB ----- - -Invoke the HTTP function: - ----- -curl https://REGION-PROJECT_ID.cloudfunctions.net/function-sample-gcp-http -d "hello" ----- - - -Setting custom HTTP statusCode: - ----- -Functions can specify a custom HTTP response code by setting the `FunctionInvoker.HTTP_STATUS_CODE` header. ----- - -[source, java] ----- -@Bean -public Function> function() { - - String payload = "hello"; - - Message message = MessageBuilder.withPayload(payload).setHeader(FunctionInvoker.HTTP_STATUS_CODE, 404).build(); - - return input -> message; -}; ----- - - - -==== Background Functions - -Google Cloud Functions also supports deploying https://cloud.google.com/functions/docs/writing/background[Background Functions] which are invoked indirectly in response to an event, such as a message on a https://cloud.google.com/pubsub[Cloud Pub/Sub] topic, a change in a https://cloud.google.com/storage[Cloud Storage] bucket, or a https://firebase.google.com/[Firebase] event. - -The `spring-cloud-function-adapter-gcp` allows for functions to be deployed as background functions as well. - -The sections below describe the process for writing a Cloud Pub/Sub topic background function. -However, there are a number of different event types that can trigger a background function to execute which are not discussed here; these are described in the https://cloud.google.com/functions/docs/calling[Background Function triggers documentation]. - -===== Getting Started - -Let’s start with a simple Spring Cloud Function which will run as a GCF background function: - -[source, java] ----- -@SpringBootApplication -public class BackgroundFunctionMain { - - public static void main(String[] args) { - SpringApplication.run(BackgroundFunctionMain.class, args); - } - - @Bean - public Consumer pubSubFunction() { - return message -> System.out.println("The Pub/Sub message data: " + message.getData()); - } -} ----- - -In addition, create `PubSubMessage` class in the project with the below definition. -This class represents the https://cloud.google.com/functions/docs/calling/pubsub#event_structure[Pub/Sub event structure] which gets passed to your function on a Pub/Sub topic event. - -[source, java] ----- -public class PubSubMessage { - - private String data; - - private Map attributes; - - private String messageId; - - private String publishTime; - - public String getData() { - return data; - } - - public void setData(String data) { - this.data = data; - } - - public Map getAttributes() { - return attributes; - } - - public void setAttributes(Map attributes) { - this.attributes = attributes; - } - - public String getMessageId() { - return messageId; - } - - public void setMessageId(String messageId) { - this.messageId = messageId; - } - - public String getPublishTime() { - return publishTime; - } - - public void setPublishTime(String publishTime) { - this.publishTime = publishTime; - } - -} ----- - -Specify your configuration main class in `resources/META-INF/MANIFEST.MF`. - -[source] ----- -Main-Class: com.example.BackgroundFunctionMain ----- - -Then run the function locally. -This is provided by the Google Cloud Functions `function-maven-plugin` described in the project dependencies section. - ----- -mvn function:run ----- - -Invoke the HTTP function: - ----- -curl localhost:8080 -H "Content-Type: application/json" -d '{"data":"hello"}' ----- - -Verify that the function was invoked by viewing the logs. - -===== Deploy to GCP - -In order to deploy your background function to GCP, first package your application. - ----- -mvn package ----- - -If you added the custom `spring-boot-maven-plugin` plugin defined above, you should see the resulting JAR in `target/deploy` directory. -This JAR is correctly formatted for deployment to Google Cloud Functions. - -Next, make sure that you have the https://cloud.google.com/sdk/install[Cloud SDK CLI] installed. - -From the project base directory run the following command to deploy. - ----- -gcloud functions deploy function-sample-gcp-background \ ---entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \ ---runtime java11 \ ---trigger-topic my-functions-topic \ ---source target/deploy \ ---memory 512MB ----- - -Google Cloud Function will now invoke the function every time a message is published to the topic specified by `--trigger-topic`. - -For a walkthrough on testing and verifying your background function, see the instructions for running the https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-gcp-background/[GCF Background Function sample]. - -==== Sample Functions - -The project provides the following sample functions as reference: - -* The https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-gcp-http/[function-sample-gcp-http] is an HTTP Function which you can test locally and try deploying. -* The https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-gcp-background/[function-sample-gcp-background] shows an example of a background function that is triggered by a message being published to a specified Pub/Sub topic. diff --git a/docs/src/main/asciidoc/adapters/gcp.adoc b/docs/src/main/asciidoc/adapters/gcp.adoc deleted file mode 100644 index 3b72f8bfe..000000000 --- a/docs/src/main/asciidoc/adapters/gcp.adoc +++ /dev/null @@ -1,3 +0,0 @@ -*{project-version}* - -include::adapters/gcp-intro.adoc[] diff --git a/docs/src/main/asciidoc/functional.adoc b/docs/src/main/asciidoc/functional.adoc deleted file mode 100644 index a12f520ac..000000000 --- a/docs/src/main/asciidoc/functional.adoc +++ /dev/null @@ -1,316 +0,0 @@ -Spring Cloud Function supports a "functional" style of bean declarations for small apps where you need fast startup. The functional style of bean declaration was a feature of Spring Framework 5.0 with significant enhancements in 5.1. - -== Comparing Functional with Traditional Bean Definitions - -Here's a vanilla Spring Cloud Function application from with the -familiar `@Configuration` and `@Bean` declaration style: - -```java -@SpringBootApplication -public class DemoApplication { - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } - - public static void main(String[] args) { - SpringApplication.run(DemoApplication.class, args); - } - -} -``` - -Now for the functional beans: the user application code can be recast into "functional" -form, like this: - -```java -@SpringBootConfiguration -public class DemoApplication implements ApplicationContextInitializer { - - public static void main(String[] args) { - FunctionalSpringApplication.run(DemoApplication.class, args); - } - - public Function uppercase() { - return value -> value.toUpperCase(); - } - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("demo", FunctionRegistration.class, - () -> new FunctionRegistration<>(uppercase()) - .type(FunctionTypeUtils.functionType(String.class, String.class))); - } - -} -``` - -The main differences are: - -* The main class is an `ApplicationContextInitializer`. - -* The `@Bean` methods have been converted to calls to `context.registerBean()` - -* The `@SpringBootApplication` has been replaced with -`@SpringBootConfiguration` to signify that we are not enabling Spring -Boot autoconfiguration, and yet still marking the class as an "entry -point". - -* The `SpringApplication` from Spring Boot has been replaced with a -`FunctionalSpringApplication` from Spring Cloud Function (it's a -subclass). - -The business logic beans that you register in a Spring Cloud Function app are of type `FunctionRegistration`. -This is a wrapper that contains both the function and information about the input and output types. In the `@Bean` -form of the application that information can be derived reflectively, but in a functional bean registration some of -it is lost unless we use a `FunctionRegistration`. - -An alternative to using an `ApplicationContextInitializer` and `FunctionRegistration` is to make the application -itself implement `Function` (or `Consumer` or `Supplier`). Example (equivalent to the above): - -```java -@SpringBootConfiguration -public class DemoApplication implements Function { - - public static void main(String[] args) { - FunctionalSpringApplication.run(DemoApplication.class, args); - } - - @Override - public String apply(String value) { - return value.toUpperCase(); - } - -} -``` - -It would also work if you add a separate, standalone class of type `Function` and register it with -the `SpringApplication` using an alternative form of the `run()` method. The main thing is that the generic -type information is available at runtime through the class declaration. - -Suppose you have -[source, java] ----- -@Component -public class CustomFunction implements Function, Flux> { - @Override - public Flux apply(Flux flux) { - return flux.map(foo -> new Bar("This is a Bar object from Foo value: " + foo.getValue())); - } - -} ----- - -You register it as such: - -[source, java] ----- -@Override -public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(new CustomFunction()).type(CustomFunction.class)); -} ----- - -== Limitations of Functional Bean Declaration - -Most Spring Cloud Function apps have a relatively small scope compared to the whole of Spring Boot, -so we are able to adapt it to these functional bean definitions easily. If you step outside that limited scope, -you can extend your Spring Cloud Function app by switching back to `@Bean` style configuration, or by using a hybrid -approach. If you want to take advantage of Spring Boot autoconfiguration for integrations with external datastores, -for example, you will need to use `@EnableAutoConfiguration`. Your functions can still be defined using the functional -declarations if you want (i.e. the "hybrid" style), but in that case you will need to explicitly switch off the "full -functional mode" using `spring.functional.enabled=false` so that Spring Boot can take back control. - -[[function_visualization]] -= Function visualization and control - -Spring Cloud Function supports visualization of functions available in `FunctionCatalog` through Actuator endpoints as well as programmatic way. - -==== Programmatic way - -To see function available within your application context programmatically all you need is access to `FunctionCatalog`. There you can -finds methods to get the size of the catalog, lookup functions as well as list the names of all the available functions. - -For example, - -[source,java] ----- -FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); -int size = functionCatalog.size(); // will tell you how many functions available in catalog -Set names = functionCatalog.getNames(null); will list the names of all the Function, Suppliers and Consumers available in catalog -. . . ----- - -==== Actuator -Since actuator and web are optional, you must first add one of the web dependencies as well as add the actuator dependency manually. -The following example shows how to add the dependency for the Web framework: - -[source,xml] ----- - - org.springframework.boot - spring-boot-starter-web - ----- - -The following example shows how to add the dependency for the WebFlux framework: - -[source,xml] ----- - - org.springframework.boot - spring-boot-starter-webflux - ----- - -You can add the Actuator dependency as follows: -[source,xml] ----- - - org.springframework.boot - spring-boot-starter-actuator - ----- - -You must also enable the `functions` actuator endpoints by setting the following property: `--management.endpoints.web.exposure.include=functions`. - -Access the following URL to see the functions in FunctionCatalog: -`http://:/actuator/functions` - -For example, -[source,text] ----- -curl http://localhost:8080/actuator/functions ----- - -Your output should look something like this: -[source,text] ----- -{"charCounter": - {"type":"FUNCTION","input-type":"string","output-type":"integer"}, - "logger": - {"type":"CONSUMER","input-type":"string"}, - "functionRouter": - {"type":"FUNCTION","input-type":"object","output-type":"object"}, - "words": - {"type":"SUPPLIER","output-type":"string"}. . . ----- - -= Testing Functional Applications - -Spring Cloud Function also has some utilities for integration testing that will be very familiar to Spring Boot users. - -Suppose this is your application: - -[source, java] ----- -@SpringBootApplication -public class SampleFunctionApplication { - - public static void main(String[] args) { - SpringApplication.run(SampleFunctionApplication.class, args); - } - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } -} ----- - -Here is an integration test for the HTTP server wrapping this application: - -[source, java] ----- -@SpringBootTest(classes = SampleFunctionApplication.class, - webEnvironment = WebEnvironment.RANDOM_PORT) -public class WebFunctionTests { - - @Autowired - private TestRestTemplate rest; - - @Test - public void test() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/uppercase")).body("hello"), String.class); - System.out.println(result.getBody()); - } -} ----- - -or when function bean definition style is used: - -[source, java] ----- -@FunctionalSpringBootTest -public class WebFunctionTests { - - @Autowired - private TestRestTemplate rest; - - @Test - public void test() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/uppercase")).body("hello"), String.class); - System.out.println(result.getBody()); - } -} ----- - -This test is almost identical to the one you would write for the `@Bean` version of the same app - the only difference -is the `@FunctionalSpringBootTest` annotation, instead of the regular `@SpringBootTest`. All the other pieces, -like the `@Autowired` `TestRestTemplate`, are standard Spring Boot features. - -And to help with correct dependencies here is the excerpt from POM - -[source, xml, subs=attributes+] ----- - - org.springframework.boot - spring-boot-starter-parent - {spring-boot-version} - - - . . . . - - org.springframework.cloud - spring-cloud-function-web - {project-version} - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-web - test - - - org.springframework.boot - spring-boot-starter-test - test - ----- - -Or you could write a test for a non-HTTP app using just the `FunctionCatalog`. For example: - -[source, java] ----- -@FunctionalSpringBootTest -public class FunctionalTests { - - @Autowired - private FunctionCatalog catalog; - - @Test - public void words() { - Function function = catalog.lookup(Function.class, - "uppercase"); - assertThat(function.apply("hello")).isEqualTo("HELLO"); - } - -} ----- diff --git a/docs/src/main/asciidoc/getting-started.adoc b/docs/src/main/asciidoc/getting-started.adoc deleted file mode 100644 index f46cbe0c7..000000000 --- a/docs/src/main/asciidoc/getting-started.adoc +++ /dev/null @@ -1,33 +0,0 @@ -Build from the command line (and "install" the samples): - ----- -$ ./mvnw clean install ----- - -(If you like to YOLO add `-DskipTests`.) - -Run one of the samples, e.g. - ----- -$ java -jar spring-cloud-function-samples/function-sample/target/*.jar ----- - -This runs the app and exposes its functions over HTTP, so you can -convert a string to uppercase, like this: - ----- -$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d Hello -HELLO ----- - -You can convert multiple strings (a `Flux`) by separating them -with new lines - ----- -$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d 'Hello -> World' -HELLOWORLD ----- - -(You can use `^Q^J` in a terminal to insert a new line in a literal -string like that.) diff --git a/docs/src/main/asciidoc/ghpages.sh b/docs/src/main/asciidoc/ghpages.sh deleted file mode 100755 index 2562c7171..000000000 --- a/docs/src/main/asciidoc/ghpages.sh +++ /dev/null @@ -1,330 +0,0 @@ -#!/bin/bash -x - -set -e - -# Set default props like MAVEN_PATH, ROOT_FOLDER etc. -function set_default_props() { - # The script should be executed from the root folder - ROOT_FOLDER=`pwd` - echo "Current folder is ${ROOT_FOLDER}" - - if [[ ! -e "${ROOT_FOLDER}/.git" ]]; then - echo "You're not in the root folder of the project!" - exit 1 - fi - - # Prop that will let commit the changes - COMMIT_CHANGES="no" - MAVEN_PATH=${MAVEN_PATH:-} - echo "Path to Maven is [${MAVEN_PATH}]" - REPO_NAME=${PWD##*/} - echo "Repo name is [${REPO_NAME}]" - SPRING_CLOUD_STATIC_REPO=${SPRING_CLOUD_STATIC_REPO:-git@github.com:spring-cloud/spring-cloud-static.git} - echo "Spring Cloud Static repo is [${SPRING_CLOUD_STATIC_REPO}" -} - -# Check if gh-pages exists and docs have been built -function check_if_anything_to_sync() { - git remote set-url --push origin `git config remote.origin.url | sed -e 's/^git:/https:/'` - - if ! (git remote set-branches --add origin gh-pages && git fetch -q); then - echo "No gh-pages, so not syncing" - exit 0 - fi - - if ! [ -d docs/target/generated-docs ] && ! [ "${BUILD}" == "yes" ]; then - echo "No gh-pages sources in docs/target/generated-docs, so not syncing" - exit 0 - fi -} - -function retrieve_current_branch() { - # Code getting the name of the current branch. For master we want to publish as we did until now - # https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch - # If there is a branch already passed will reuse it - otherwise will try to find it - CURRENT_BRANCH=${BRANCH} - if [[ -z "${CURRENT_BRANCH}" ]] ; then - CURRENT_BRANCH=$(git symbolic-ref -q HEAD) - CURRENT_BRANCH=${CURRENT_BRANCH##refs/heads/} - CURRENT_BRANCH=${CURRENT_BRANCH:-HEAD} - fi - echo "Current branch is [${CURRENT_BRANCH}]" - git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script" -} - -# Switches to the provided value of the release version. We always prefix it with `v` -function switch_to_tag() { - git checkout v${VERSION} -} - -# Build the docs if switch is on -function build_docs_if_applicable() { - if [[ "${BUILD}" == "yes" ]] ; then - ./mvnw clean install -P docs -pl docs -DskipTests - fi -} - -# Get the name of the `docs.main` property -# Get whitelisted branches - assumes that a `docs` module is available under `docs` profile -function retrieve_doc_properties() { - MAIN_ADOC_VALUE=$("${MAVEN_PATH}"mvn -q \ - -Dexec.executable="echo" \ - -Dexec.args='${docs.main}' \ - --non-recursive \ - org.codehaus.mojo:exec-maven-plugin:1.3.1:exec) - echo "Extracted 'main.adoc' from Maven build [${MAIN_ADOC_VALUE}]" - - - WHITELIST_PROPERTY=${WHITELIST_PROPERTY:-"docs.whitelisted.branches"} - WHITELISTED_BRANCHES_VALUE=$("${MAVEN_PATH}"mvn -q \ - -Dexec.executable="echo" \ - -Dexec.args="\${${WHITELIST_PROPERTY}}" \ - org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \ - -P docs \ - -pl docs) - echo "Extracted '${WHITELIST_PROPERTY}' from Maven build [${WHITELISTED_BRANCHES_VALUE}]" -} - -# Stash any outstanding changes -function stash_changes() { - git diff-index --quiet HEAD && dirty=$? || (echo "Failed to check if the current repo is dirty. Assuming that it is." && dirty="1") - if [ "$dirty" != "0" ]; then git stash; fi -} - -# Switch to gh-pages branch to sync it with current branch -function add_docs_from_target() { - local DESTINATION_REPO_FOLDER - if [[ -z "${DESTINATION}" && -z "${CLONE}" ]] ; then - DESTINATION_REPO_FOLDER=${ROOT_FOLDER} - elif [[ "${CLONE}" == "yes" ]]; then - mkdir -p ${ROOT_FOLDER}/target - local clonedStatic=${ROOT_FOLDER}/target/spring-cloud-static - if [[ ! -e "${clonedStatic}/.git" ]]; then - echo "Cloning Spring Cloud Static to target" - git clone ${SPRING_CLOUD_STATIC_REPO} ${clonedStatic} && git checkout gh-pages - else - echo "Spring Cloud Static already cloned - will pull changes" - cd ${clonedStatic} && git checkout gh-pages && git pull origin gh-pages - fi - DESTINATION_REPO_FOLDER=${clonedStatic}/${REPO_NAME} - mkdir -p ${DESTINATION_REPO_FOLDER} - else - if [[ ! -e "${DESTINATION}/.git" ]]; then - echo "[${DESTINATION}] is not a git repository" - exit 1 - fi - DESTINATION_REPO_FOLDER=${DESTINATION}/${REPO_NAME} - mkdir -p ${DESTINATION_REPO_FOLDER} - echo "Destination was provided [${DESTINATION}]" - fi - cd ${DESTINATION_REPO_FOLDER} - git checkout gh-pages - git pull origin gh-pages - - # Add git branches - ################################################################### - if [[ -z "${VERSION}" ]] ; then - copy_docs_for_current_version - else - copy_docs_for_provided_version - fi - commit_changes_if_applicable -} - - -# Copies the docs by using the retrieved properties from Maven build -function copy_docs_for_current_version() { - if [[ "${CURRENT_BRANCH}" == "master" ]] ; then - echo -e "Current branch is master - will copy the current docs only to the root folder" - for f in docs/target/generated-docs/*; do - file=${f#docs/target/generated-docs/*} - if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then - # Not ignored... - cp -rf $f ${ROOT_FOLDER}/ - git add -A ${ROOT_FOLDER}/$file - fi - done - COMMIT_CHANGES="yes" - else - echo -e "Current branch is [${CURRENT_BRANCH}]" - # https://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin - if [[ ",${WHITELISTED_BRANCHES_VALUE}," = *",${CURRENT_BRANCH},"* ]] ; then - mkdir -p ${ROOT_FOLDER}/${CURRENT_BRANCH} - echo -e "Branch [${CURRENT_BRANCH}] is whitelisted! Will copy the current docs to the [${CURRENT_BRANCH}] folder" - for f in docs/target/generated-docs/*; do - file=${f#docs/target/generated-docs/*} - if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then - # Not ignored... - # We want users to access 2.0.0.BUILD-SNAPSHOT/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html - if [[ "${file}" == "${MAIN_ADOC_VALUE}.html" ]] ; then - # We don't want to copy the spring-cloud-sleuth.html - # we want it to be converted to index.html - cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html - git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html - else - cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH} - git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/$file - fi - fi - done - COMMIT_CHANGES="yes" - else - echo -e "Branch [${CURRENT_BRANCH}] is not on the white list! Check out the Maven [${WHITELIST_PROPERTY}] property in - [docs] module available under [docs] profile. Won't commit any changes to gh-pages for this branch." - fi - fi -} - -# Copies the docs by using the explicitly provided version -function copy_docs_for_provided_version() { - local FOLDER=${DESTINATION_REPO_FOLDER}/${VERSION} - mkdir -p ${FOLDER} - echo -e "Current tag is [v${VERSION}] Will copy the current docs to the [${FOLDER}] folder" - for f in ${ROOT_FOLDER}/docs/target/generated-docs/*; do - file=${f#${ROOT_FOLDER}/docs/target/generated-docs/*} - copy_docs_for_branch ${file} ${FOLDER} - done - COMMIT_CHANGES="yes" - CURRENT_BRANCH="v${VERSION}" -} - -# Copies the docs from target to the provided destination -# Params: -# $1 - file from target -# $2 - destination to which copy the files -function copy_docs_for_branch() { - local file=$1 - local destination=$2 - if ! git ls-files -i -o --exclude-standard --directory | grep -q ^${file}$; then - # Not ignored... - # We want users to access 2.0.0.BUILD-SNAPSHOT/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html - if [[ ("${file}" == "${MAIN_ADOC_VALUE}.html") || ("${file}" == "${REPO_NAME}.html") ]] ; then - # We don't want to copy the spring-cloud-sleuth.html - # we want it to be converted to index.html - cp -rf $f ${destination}/index.html - git add -A ${destination}/index.html - else - cp -rf $f ${destination} - git add -A ${destination}/$file - fi - fi -} - -function commit_changes_if_applicable() { - if [[ "${COMMIT_CHANGES}" == "yes" ]] ; then - COMMIT_SUCCESSFUL="no" - git commit -a -m "Sync docs from ${CURRENT_BRANCH} to gh-pages" && COMMIT_SUCCESSFUL="yes" || echo "Failed to commit changes" - - # Uncomment the following push if you want to auto push to - # the gh-pages branch whenever you commit to master locally. - # This is a little extreme. Use with care! - ################################################################### - if [[ "${COMMIT_SUCCESSFUL}" == "yes" ]] ; then - git push origin gh-pages - fi - fi -} - -# Switch back to the previous branch and exit block -function checkout_previous_branch() { - # If -version was provided we need to come back to root project - cd ${ROOT_FOLDER} - git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script" - if [ "$dirty" != "0" ]; then git stash pop; fi - exit 0 -} - -# Assert if properties have been properly passed -function assert_properties() { -echo "VERSION [${VERSION}], DESTINATION [${DESTINATION}], CLONE [${CLONE}]" -if [[ "${VERSION}" != "" && (-z "${DESTINATION}" && -z "${CLONE}") ]] ; then echo "Version was set but destination / clone was not!"; exit 1;fi -if [[ ("${DESTINATION}" != "" && "${CLONE}" != "") && -z "${VERSION}" ]] ; then echo "Destination / clone was set but version was not!"; exit 1;fi -if [[ "${DESTINATION}" != "" && "${CLONE}" == "yes" ]] ; then echo "Destination and clone was set. Pick one!"; exit 1;fi -} - -# Prints the usage -function print_usage() { -cat </` -- if the destination switch is passed (-d) then the script will check if the provided dir is a git repo and then will - switch to gh-pages of that repo and copy the generated docs to `docs//` - -USAGE: - -You can use the following options: - --v|--version - the script will apply the whole procedure for a particular library version --d|--destination - the root of destination folder where the docs should be copied. You have to use the full path. - E.g. point to spring-cloud-static folder. Can't be used with (-c) --b|--build - will run the standard build process after checking out the branch --c|--clone - will automatically clone the spring-cloud-static repo instead of providing the destination. - Obviously can't be used with (-d) - -EOF -} - - -# ========================================== -# ____ ____ _____ _____ _____ _______ -# / ____|/ ____| __ \|_ _| __ \__ __| -# | (___ | | | |__) | | | | |__) | | | -# \___ \| | | _ / | | | ___/ | | -# ____) | |____| | \ \ _| |_| | | | -# |_____/ \_____|_| \_\_____|_| |_| -# -# ========================================== - -while [[ $# > 0 ]] -do -key="$1" -case ${key} in - -v|--version) - VERSION="$2" - shift # past argument - ;; - -d|--destination) - DESTINATION="$2" - shift # past argument - ;; - -b|--build) - BUILD="yes" - ;; - -c|--clone) - CLONE="yes" - ;; - -h|--help) - print_usage - exit 0 - ;; - *) - echo "Invalid option: [$1]" - print_usage - exit 1 - ;; -esac -shift # past argument or value -done - -assert_properties -set_default_props -check_if_anything_to_sync -if [[ -z "${VERSION}" ]] ; then - retrieve_current_branch -else - switch_to_tag -fi -build_docs_if_applicable -retrieve_doc_properties -stash_changes -add_docs_from_target -checkout_previous_branch \ No newline at end of file diff --git a/docs/src/main/asciidoc/images/AWS-deploy.png b/docs/src/main/asciidoc/images/AWS-deploy.png deleted file mode 100644 index c840e1396..000000000 Binary files a/docs/src/main/asciidoc/images/AWS-deploy.png and /dev/null differ diff --git a/docs/src/main/asciidoc/images/remove.txt b/docs/src/main/asciidoc/images/remove.txt deleted file mode 100644 index bbd40769d..000000000 --- a/docs/src/main/asciidoc/images/remove.txt +++ /dev/null @@ -1 +0,0 @@ -please remove once this directory is not empty \ No newline at end of file diff --git a/docs/src/main/asciidoc/images/scf-azure-adapter.svg b/docs/src/main/asciidoc/images/scf-azure-adapter.svg deleted file mode 100644 index 8a064d8f4..000000000 --- a/docs/src/main/asciidoc/images/scf-azure-adapter.svg +++ /dev/null @@ -1,272 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/main/asciidoc/index.adoc b/docs/src/main/asciidoc/index.adoc deleted file mode 100644 index ebc517b8a..000000000 --- a/docs/src/main/asciidoc/index.adoc +++ /dev/null @@ -1,23 +0,0 @@ -= Spring Cloud Function Reference Documentation -Mark Fisher, Dave Syer, Oleg Zhurakousky, Anshul Mehra, Dan Dobrin, Chris Bono, Artem Bilan - -*{project-version}* - -:docinfo: shared - -The reference documentation consists of the following sections: - -[horizontal] -<> :: Spring Cloud Function Reference -https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-cloudevent[Cloud Events] :: Cloud Events -https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-rsocket[RSocket] :: RSocket -<<./spring-integration.adoc#spring-integration,Spring Integration>> :: Spring Integration Framework Interaction -<> :: AWS Adapter Reference -<> :: Azure Adapter Reference -<> :: GCP Adapter Reference - - -Relevant Links: - -[horizontal] -https://projectreactor.io/[Reactor] :: Project Reactor diff --git a/docs/src/main/asciidoc/sagan-index.adoc b/docs/src/main/asciidoc/sagan-index.adoc deleted file mode 100644 index fac997ff1..000000000 --- a/docs/src/main/asciidoc/sagan-index.adoc +++ /dev/null @@ -1,46 +0,0 @@ -Spring Cloud Function is a project with the following high-level goals: - -* Promote the implementation of business logic via functions. -* Decouple the development lifecycle of business logic from any specific runtime target so that the same code can run as a web endpoint, a stream processor, or a task. -* Support a uniform programming model across serverless providers, as well as the ability to run standalone (locally or in a PaaS). -* Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers. - -It abstracts away all of the transport details and infrastructure, allowing the developer to keep all the familiar tools and processes, and focus firmly on business logic. - -## Features - -Spring Cloud Function features: - -* _Choice of programming styles - reactive, imperative or hybrid._ -* _Function composition and adaptation (e.g., composing imperative functions with reactive)._ -* _Support for reactive function with multiple inputs and outputs allowing merging, joining and other complex streaming operation to be handled by functions._ -* _Transparent type conversion of inputs and outputs._ -* _Packaging functions for deployments, specific to the target platform (e.g., Project Riff, AWS Lambda and more)_ -* _Adapters to expose function to the outside world as HTTP endpoints etc._ -* _Deploying a JAR file containing such an application context with an isolated classloader, so that you can pack them together in a single JVM._ -* _Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Microsoft Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp[Google Cloud Functions], and possibly other "serverless" service providers._ - -Here's a complete, executable, testable Spring Boot application (implementing a simple string manipulation): - -```java -@SpringBootApplication -public class Application { - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } - - @Bean - public Function, Flux> uppercase() { - return flux -> flux.map(value -> value.toUpperCase()); - } -} -``` -### Sample Projects - -* https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample[Vanilla] -* https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample-pof[Plain Old Function] -* https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-aws[AWS Lambda] -* https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-azure[Microsoft Azure] -* https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-gcp-http[Google Cloud Functions] - - diff --git a/docs/src/main/asciidoc/spring-cloud-function.adoc b/docs/src/main/asciidoc/spring-cloud-function.adoc deleted file mode 100644 index 7eea6e5fc..000000000 --- a/docs/src/main/asciidoc/spring-cloud-function.adoc +++ /dev/null @@ -1,1031 +0,0 @@ -= Spring Cloud Function - -Mark Fisher, Dave Syer, Oleg Zhurakousky, Anshul Mehra, Dan Dobrin - -*{project-version}* - ---- - -:github: https://github.com/spring-cloud/spring-cloud-function -:githubmaster: {github}/tree/master -:docslink: {githubmaster}/docs/src/main/asciidoc -:nofooter: -:branch: master - -== Introduction - -include::_intro.adoc[] - -include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing-docs.adoc[] - -== Getting Started - -include::getting-started.adoc[] - -== Programming model - -[[function.catalog]] - -=== Function Catalog and Flexible Function Signatures - -One of the main features of Spring Cloud Function is to adapt and support a range of type signatures for user-defined functions, -while providing a consistent execution model. -That's why all user defined functions are transformed into a canonical representation by `FunctionCatalog`. - -While users don't normally have to care about the `FunctionCatalog` at all, it is useful to know what -kind of functions are supported in user code. - -It is also important to understand that Spring Cloud Function provides first class support for reactive API -provided by https://projectreactor.io/[Project Reactor] allowing reactive primitives such as `Mono` and `Flux` -to be used as types in user defined functions providing greater flexibility when choosing programming model for -your function implementation. -Reactive programming model also enables functional support for features that would be otherwise difficult to impossible to implement -using imperative programming style. For more on this please read <> section. - -=== Java 8 function support - -Spring Cloud Function embraces and builds on top of the 3 core functional interfaces defined by Java -and available to us since Java 8. - -- Supplier -- Function -- Consumer - -To avoid constantly mentioning `Supplier`, `Function` and `Consumer` we’ll refer to them a Functional beans for the rest of this manual where appropriate. - -In a nutshell, any bean in your Application Context that is Functional bean will lazily be registered with `FunctionCatalog`. -This means that it could benefit from all of the additional features described in this reference manual. - -In a simplest of application all you need to do is to declare `@Bean` of type `Supplier`, `Function` or `Consumer` in your application configuration. -Then you can access `FunctionCatalog` and lookup a particular function based on its name. - -For example: - - -[source, test] ----- -@Bean -public Function uppercase() { - return value -> value.toUpperCase(); -} - -. . . - -FunctionCatalog catalog = applicationContext.getBean(FunctionCatalog.class); -Function uppercase = catalog.lookup(“uppercase”); ----- - -Important to understand that given that `uppercase` is a bean, you can certainly get it form the `ApplicationContext` directly, but all you will get is just your bean as you declared it without any extra features provided by SCF. When you do lookup of a function via `FunctionCatalog`, the instance you will receive is wrapped (instrumented) with additional features (i.e., type conversion, composition etc.) described in this manual. Also, it is important to understand that a typical user does not use Spring Cloud Function directly. Instead a typical user implements Java `Function/Supplier/Consumer` with the idea of using it in different execution contexts without additional work. For example the same java function could be represented as _REST endpoint_ or _Streaming message handler_ or _AWS Lambda_ and more via Spring Cloud Function provided -adapters as well as other frameworks using Spring Cloud Function as the core programming model (e.g., https://spring.io/projects/spring-cloud-stream[Spring Cloud Stream]) -So in summary Spring Cloud Function instruments java functions with additional features to be utilised in variety of execution contexts. - - -==== Function definition -While the previous example shows you how to lookup function in FunctionCatalog programmatically, in a typical integration case where Spring Cloud Function used as programming model by another framework (e.fg. Spring Cloud Stream), you declare which functions to use via `spring.cloud.function.definition` property. Knowing that it is important to understand some default behaviour when it comes to discovering functions in `FunctionCatalog`. For example, if you only have one Functional bean in your `ApplicationContext`, the `spring.cloud.function.definition` property typically will not be required, since a single function in `FunctionCatalog` can be looked up by an empty name or any name. For example, assuming that `uppercase` is the only function in your catalog, it can be looked up as `catalog.lookup(null)`, `catalog.lookup(“”)`, `catalog.lookup(“foo”)` -That said, for cases where you are using framework such as Spring Cloud Stream which uses `spring.cloud.function.definition` it is best practice and recommended to always use `spring.cloud.function.definition` property. - -For example, - -[source, test] ----- -spring.cloud.function.definition=uppercase ----- - -==== Filtering ineligible functions -A typical Application Context may include beans that are valid java functions, but not intended to be candidates to be registered with `FunctionCatalog`. -Such beans could be auto-configurations from other projects or any other beans that qualify to be Java functions. -The framework provides default filtering of known beans that should not be candidates for registration with function catalog. -You can also add to this list additional beans by providing coma delimited list of bean definition names using -`spring.cloud.function.ineligible-definitions` property. - -For example, - -[source, test] ----- -spring.cloud.function.ineligible-definitions=foo,bar ----- - -==== Supplier -Supplier can be _reactive_ - `Supplier>` -or _imperative_ - `Supplier`. From the invocation standpoint this should make no difference -to the implementor of such Supplier. However, when used within frameworks -(e.g., https://spring.io/projects/spring-cloud-stream[Spring Cloud Stream]), Suppliers, especially reactive, -often used to represent the source of the stream, therefore they are invoked once to get the stream (e.g., Flux) -to which consumers can subscribe to. In other words such suppliers represent an equivalent of an _infinite stream_. -However, the same reactive suppliers can also represent _finite_ stream(s) (e.g., result set on the polled JDBC data). -In those cases such reactive suppliers must be hooked up to some polling mechanism of the underlying framework. - -To assist with that Spring Cloud Function provides a marker annotation -`org.springframework.cloud.function.context.PollableBean` to signal that such supplier produces a -finite stream and may need to be polled again. That said, it is important to understand that Spring Cloud Function itself -provides no behavior for this annotation. - -In addition `PollableBean` annotation exposes a _splittable_ attribute to signal that produced stream -needs to be split (see https://www.enterpriseintegrationpatterns.com/patterns/messaging/Sequencer.html[Splitter EIP]) - -Here is the example: - -[source, java] ----- -@PollableBean(splittable = true) -public Supplier> someSupplier() { - return () -> { - String v1 = String.valueOf(System.nanoTime()); - String v2 = String.valueOf(System.nanoTime()); - String v3 = String.valueOf(System.nanoTime()); - return Flux.just(v1, v2, v3); - }; -} ----- - -==== Function -Function can also be written in imperative or reactive way, yet unlike Supplier and Consumer there are -no special considerations for the implementor other then understanding that when used within frameworks -such as https://spring.io/projects/spring-cloud-stream[Spring Cloud Stream] and others, reactive function is -invoked only once to pass a reference to the stream (Flux or Mono) and imperative is invoked once per event. - -==== Consumer -Consumer is a little bit special because it has a `void` return type, -which implies blocking, at least potentially. Most likely you will not -need to write `Consumer>`, but if you do need to do that, -remember to subscribe to the input flux. - -=== Function Composition -Function Composition is a feature that allows one to compose several functions into one. -The core support is based on function composition feature available with https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html#andThen-java.util.function.Function-[Function.andThen(..)] -support available since Java 8. However on top of it, we provide few additional features. - -==== Declarative Function Composition - -This feature allows you to provide composition instruction in a declarative way using `|` (pipe) or `,` (comma) delimiter -when providing `spring.cloud.function.definition` property. - -For example ----- ---spring.cloud.function.definition=uppercase|reverse ----- -Here we effectively provided a definition of a single function which itself is a composition of -function `uppercase` and function `reverse`. In fact that is one of the reasons why the property name is _definition_ and not _name_, -since the definition of a function can be a composition of several named functions. -And as mentioned you can use `,` instead of pipe (such as `...definition=uppercase,reverse`). - -==== Composing non-Functions -Spring Cloud Function also supports composing Supplier with `Consumer` or `Function` as well as `Function` with `Consumer`. -What's important here is to understand the end product of such definitions. -Composing Supplier with Function still results in Supplier while composing Supplier with Consumer will effectively render Runnable. -Following the same logic composing Function with Consumer will result in Consumer. - -And of course you can't compose uncomposable such as Consumer and Function, Consumer and Supplier etc. - - -=== Function Routing and Filtering - -Since version 2.2 Spring Cloud Function provides routing feature allowing -you to invoke a single function which acts as a router to an actual function you wish to invoke -This feature is very useful in certain FAAS environments where maintaining configurations -for several functions could be cumbersome or exposing more than one function is not possible. - -The `RoutingFunction` is registered in _FunctionCatalog_ under the name `functionRouter`. For simplicity -and consistency you can also refer to `RoutingFunction.FUNCTION_NAME` constant. - -This function has the following signature: - -[source, java] ----- -public class RoutingFunction implements Function { -. . . -} ----- -The routing instructions could be communicated in several ways. We support providing instructions via Message headers, System -properties as well as pluggable strategy. So let's look at some of the details - -==== MessageRoutingCallback - -The `MessageRoutingCallback` is a strategy to assist with determining the name of the route-to function definition. - -[source, java] ----- -public interface MessageRoutingCallback { - FunctionRoutingResult routingResult(Message message); - . . . -} ----- - -All you need to do is implement and register it as a bean to be picked up by the `RoutingFunction`. -For example: - -[source, java] ----- -@Bean -public MessageRoutingCallback customRouter() { - return new MessageRoutingCallback() { - @Override - public FunctionRoutingResult routingResult(Message message) { - return new FunctionRoutingResult((String) message.getHeaders().get("func_name")); - } - }; -} ----- - -In the preceding example you can see a very simple implementation of `MessageRoutingCallback` which determines the function definition from -`func_name` Message header of the incoming Message and returns the instance of `FunctionRoutingResult` containing the definition of function to invoke. - -*Message Headers* - -If the input argument is of type `Message`, you can communicate routing instruction by setting one of -`spring.cloud.function.definition` or `spring.cloud.function.routing-expression` Message headers. -As the name of the property suggests `spring.cloud.function.routing-expression` relies on Spring Expression Language (SpEL). -For more static cases you can use `spring.cloud.function.definition` header which allows you to provide -the name of a single function (e.g., `...definition=foo`) or a composition instruction (e.g., `...definition=foo|bar|baz`). -For more dynamic cases you can use `spring.cloud.function.routing-expression` header and provide SpEL expression that should resolve -into definition of a function (as described above). - -NOTE: SpEL evaluation context's root object is the -actual input argument, so in the case of `Message` you can construct expression that has access -to both `payload` and `headers` (e.g., `spring.cloud.function.routing-expression=headers.function_name`). - -IMPORTANT: SpEL allows user to provide string representation of Java code to be executed. Given that the `spring.cloud.function.routing-expression` could be provided via Message headers means that ability to set such expression could be exposed to the end user (i.e., HTTP Headers when using web module) which could result in some problems (e.g., malicious code). To manage that, all expressions coming via Message headers will only be evaluated against `SimpleEvaluationContext` which has limited functionality and designed to only evaluate the context object (Message in our case). On the other hand, all expressions that are set via property or system variable are evaluated against `StandardEvaluationContext`, which allows for full flexibility of Java language. -While setting expression via system/application property or environment variable is generally considered to be secure as it is not exposed to the end user in normal cases, there are cases where visibility as well as capability to update system, application and environment variables are indeed exposed to the end user via Spring Boot Actuator endpoints provided either by some of the Spring projects or third parties or custom implementation by the end user. Such endpoints must be secured using industry standard web security practices. -Spring Cloud Function does not expose any of such endpoints. - -In specific execution environments/models the adapters are responsible to translate and communicate -`spring.cloud.function.definition` and/or `spring.cloud.function.routing-expression` via Message header. -For example, when using _spring-cloud-function-web_ you can provide `spring.cloud.function.definition` as an HTTP -header and the framework will propagate it as well as other HTTP headers as Message headers. - -*Application Properties* - -Routing instruction can also be communicated via `spring.cloud.function.definition` -or `spring.cloud.function.routing-expression` as application properties. The rules described in the -previous section apply here as well. The only difference is you provide these instructions as -application properties (e.g., `--spring.cloud.function.definition=foo`). - -NOTE: It is important to understand that providing `spring.cloud.function.definition` -or `spring.cloud.function.routing-expression` as Message headers will only work for imperative functions (e.g., `Function`). -That is to say that we can _only_ route ***per-message*** with imperative functions. With reactive functions we can not route -***per-message***. Therefore you can only provide your routing instructions as Application Properties. -It's all about unit-of-work. In imperative function unit of work is Message so we can route based on such unit-of-work. -With reactive function unit-of-work is the entire stream, so we'll act only on the instruction provided via application -properties and route the entire stream. - -*Order of priority for routing instructions* - -Given that we have several mechanisms of providing routing instructions it is important to understand the priorities for -conflict resolutions in the event multiple mechanisms are used at the same time, so here is the order: - -1. `MessageRoutingCallback` (If function is imperative will take over regardless if anything else is defined) -2. Message Headers (If function is imperative and no `MessageRoutingCallback` provided) -3. Application Properties (Any function) - -*Unroutable Messages* - -In the event route-to function is not available in catalog you will get an exception stating that. - -There are cases when such behavior is not desired and you may want to have some "catch-all" type function which can handle such messages. -To accomplish that, framework provides `org.springframework.cloud.function.context.DefaultMessageRoutingHandler` strategy. All you need to do is register it as a bean. -Its default implementation will simply log the fact that the message is un-routable, but will allow message flow to proceed without the exception, effectively dropping the un-routable message. -If you want something more sophisticated all you need to do is provide your own implementation of this strategy and register it as a bean. - -[source, java] ----- -@Bean -public DefaultMessageRoutingHandler defaultRoutingHandler() { - return new DefaultMessageRoutingHandler() { - @Override - public void accept(Message message) { - // do something really cool - } - }; -} ----- - -==== Function Filtering -Filtering is the type of routing where there are only two paths - 'go' or 'discard'. In terms of functions it mean -you only want to invoke a certain function if some condition returns 'true', otherwise you want to discard input. -However, when it comes to discarding input there are many interpretation of what it could mean in the context of your application. -For example, you may want to log it, or you may want to maintain the counter of discarded messages. you may also want to do nothing at all. -Because of these different paths, we do not provide a general configuration option for how to deal with discarded messages. -Instead we simply recommend to define a simple Consumer which would signify the 'discard' path: - -[source, java] ----- -@Bean -public Consumer devNull() { - // log, count or whatever -} ----- -Now you can have routing expression that really only has two paths effectively becoming a filter. For example: - -[source, text] ----- ---spring.cloud.function.routing-expression=headers.contentType.toString().equals('text/plain') ? 'echo' : 'devNull' ----- -Every message that does not fit criteria to go to 'echo' function will go to 'devNull' where you can simply do nothing with it. -The signature `Consumer` will also ensure that no type conversion will be attempted resulting in almost no execution overhead. - - -IMPORTANT: When dealing with reactive inputs (e.g., Publisher), routing instructions must only be provided via Function properties. This is -due to the nature of the reactive functions which are invoked only once to pass a Publisher and the rest -is handled by the reactor, hence we can not access and/or rely on the routing instructions communicated via individual -values (e.g., Message). - -==== Multiple Routers - -By default the framework will always have a single routing function configured as described in previous sections. However, there are times when you may need more than one routing function. -In that case you can create your own instance of the `RoutingFunction` bean in addition to the existing one as long as you give it a name other than `functionRouter`. - -You can pass `spring.cloud.function.routing-expression` or `spring.cloud.function.definition` to RoutinFunction as key/value pairs in the map. - -Here is a simple example - ----- -@Configuration -protected static class MultipleRouterConfiguration { - - @Bean - RoutingFunction mySpecialRouter(FunctionCatalog functionCatalog, BeanFactory beanFactory, @Nullable MessageRoutingCallback routingCallback) { - Map propertiesMap = new HashMap<>(); - propertiesMap.put(FunctionProperties.PREFIX + ".routing-expression", "'reverse'"); - return new RoutingFunction(functionCatalog, propertiesMap, new BeanFactoryResolver(beanFactory), routingCallback); - } - - @Bean - public Function reverse() { - return v -> new StringBuilder(v).reverse().toString(); - } - - @Bean - public Function uppercase() { - return String::toUpperCase; - } -} ----- - -and a test that demonstrates how it works - -` ----- -@Test -public void testMultipleRouters() { - System.setProperty(FunctionProperties.PREFIX + ".routing-expression", "'uppercase'"); - FunctionCatalog functionCatalog = this.configureCatalog(MultipleRouterConfiguration.class); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello").build(); - assertThat(function.apply(message)).isEqualTo("HELLO"); - - function = functionCatalog.lookup("mySpecialRouter"); - assertThat(function).isNotNull(); - message = MessageBuilder.withPayload("hello").build(); - assertThat(function.apply(message)).isEqualTo("olleh"); -} ----- - -=== Input/Output Enrichment - -There are often times when you need to modify or refine an incoming or outgoing Message and to keep your code clean of non-functional concerns. You don’t want to do it inside of your business logic. - -You can always accomplish it via <>. Such approach provides several benefits: - -- It allows you to isolate this non-functional concern into a separate function which you can compose with the business function as function definition. -- It provides you with complete freedom (and danger) as to what you can modify before incoming message reaches the actual business function. - -[source, java] ----- -@Bean -public Function, Message> enrich() { - return message -> MessageBuilder.fromMessage(message).setHeader("foo", "bar").build(); -} - -@Bean -public Function, Message> myBusinessFunction() { - // do whatever -} ----- - -And then compose your function by providing the following function definition `enrich|myBusinessFunction`. - -While the described approach is the most flexible, it is also the most involved as it requires you to write some code, make it a bean or -manually register it as a function before you can compose it with the business function as you can see from the preceding example. - -But what if modifications (enrichments) you are trying to make are trivial as they are in the preceding example? Is there a simpler and more dynamic and configurable - mechanism to accomplish the same? - -Since version 3.1.3, the framework allows you to provide SpEL expression to enrich individual message headers for both input going into function and -and output coming out of it. Let’s look at one of the tests as the example. - -[source, java] ----- -@Test -public void testMixedInputOutputHeaderMapping() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut1='hello1'", - "--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut2=headers.contentType", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key1=headers.path.split('/')[0]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key2=headers.path.split('/')[1]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key3=headers.path")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("split"); - Message result = (Message) function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") - .setHeader("path", "foo/bar/baz").build()); - assertThat(result.getHeaders().containsKey("keyOut1")).isTrue(); - assertThat(result.getHeaders().get("keyOut1")).isEqualTo("hello1"); - assertThat(result.getHeaders().containsKey("keyOut2")).isTrue(); - assertThat(result.getHeaders().get("keyOut2")).isEqualTo("application/json"); - } -} ----- - -Here you see a properties called `input-header-mapping-expression` and `output-header-mapping-expression` preceded by the name of the function (i.e., `split`) and followed by the name of the message header key you want to set and the value as SpEL expression. The first expression (for 'keyOut1') is literal SpEL expressions enclosed in single quotes, effectively setting 'keyOut1' to value `hello1`. The `keyOut2` is set to the value of existing 'contentType' header. - -You can also observe some interesting features in the input header mapping where we actually splitting a value of the existing header 'path', setting individual values of key1 and key2 to the values of split elements based on the index. - -NOTE: if for whatever reason the provided expression evaluation fails, the execution of the function will proceed as if nothing ever happen. -However you will see the WARN message in your logs informing you about it - -[source, text] ----- -o.s.c.f.context.catalog.InputEnricher : Failed while evaluating expression "hello1" on incoming message. . . ----- - -In the event you are dealing with functions that have multiple inputs (next section), you can use index immediately after `input-header-mapping-expression` - -[source, text] ----- ---spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key1=‘hello1' ---spring.cloud.function.configuration.echo.input-header-mapping-expression[1].key2='hello2' ----- - -=== Function Arity - -There are times when a stream of data needs to be categorized and organized. For example, -consider a classic big-data use case of dealing with unorganized data containing, let’s say, -‘orders’ and ‘invoices’, and you want each to go into a separate data store. -This is where function arity (functions with multiple inputs and outputs) support -comes to play. - -Let’s look at an example of such a function (full implementation details are available -https://github.com/spring-cloud/spring-cloud-stream/blob/master/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/function/MultipleInputOutputFunctionTests.java#L342[here]), - -[source, java] ----- -@Bean -public Function, Tuple2, Flux>> organise() { - return flux -> ...; -} ----- - -Given that Project Reactor is a core dependency of SCF, we are using its Tuple library. -Tuples give us a unique advantage by communicating to us both _cardinality_ and _type_ information. -Both are extremely important in the context of SCSt. Cardinality lets us know -how many input and output bindings need to be created and bound to the corresponding -inputs and outputs of a function. Awareness of the type information ensures proper type -conversion. - -Also, this is where the ‘index’ part of the naming convention for binding -names comes into play, since, in this function, the two output binding -names are `organise-out-0` and `organise-out-1`. - -IMPORTANT: IMPORTANT: At the moment, function arity is *only* supported for reactive functions -(`Function...>, TupleN...>>`) centered on Complex event processing -where evaluation and computation on confluence of events typically requires view into a -stream of events rather than single event. - -=== Input Header propagation - -In a typical scenario input Message headers are not propagated to output and rightfully so, since the output of a function may be an input to something else requiring it's own set of Message headers. -However, there are times when such propagation may be necessary so Spring Cloud Function provides several mechanisms to accomplish this. - -First you can always copy headers manually. For example, if you have a Function with the signature that takes `Message` and returns `Message` (i.e., `Function`), you can simply and selectively copy headers yourselves. Remember, if your function returns Message, the framework will not do anything to it other then properly converting its payload. -However, such approach may prove to be a bit tedious, especially in cases when you simply want to copy all headers. -To assist with cases like this we provide a simple property that would allow you to set a boolean flag on a function where you want input headers to be propagated. -The property is `copy-input-headers`. - -For example, let's assume you have the following configuration: - -[source, java] ----- -@EnableAutoConfiguration -@Configuration -protected static class InputHeaderPropagationConfiguration { - - @Bean - public Function uppercase() { - return x -> x.toUpperCase(); - } -} ----- - -As you know you can still invoke this function by sending a Message to it (framework will take care of type conversion and payload extraction) - -By simply setting `spring.cloud.function.configuration.uppercase.copy-input-headers` to `true`, the following assertion will be true as well - ----- -Function, Message> uppercase = catalog.lookup("uppercase", "application/json"); -Message result = uppercase.apply(MessageBuilder.withPayload("bob").setHeader("foo", "bar").build()); -assertThat(result.getHeaders()).containsKey("foo"); ----- - -=== Type conversion (Content-Type negotiation) - -Content-Type negotiation is one of the core features of Spring Cloud Function as it allows to not only transform the incoming data to the types declared -by the function signature, but to do the same transformation during function composition making otherwise un-composable (by type) functions composable. - -To better understand the mechanics and the necessity behind content-type negotiation, we take a look at a very simple use case by -using the following function as an example: - -[source, java] ----- -@Bean -public Function personFunction {..} ----- - -The function shown in the preceding example expects a `Person` object as an argument and produces a String type as an output. If such function is -invoked with the type `Person`, than all works fine. But typically function plays a role of a handler for the incoming data which most often comes -in the raw format such as `byte[]`, `JSON String` etc. In order for the framework to succeed in passing the incoming data as an argument to -this function, it has to somehow transform the incoming data to a `Person` type. - -Spring Cloud Function relies on two native to Spring mechanisms to accomplish that. - -. _MessageConverter_ - to convert from incoming Message data to a type declared by the function. -. _ConversionService_ - to convert from incoming non-Message data to a type declared by the function. - -This means that depending on the type of the raw data (Message or non-Message) Spring Cloud Function will apply one or the other mechanisms. - -For most cases when dealing with functions that are invoked as part of some other request (e.g., HTTP, Messaging etc) the framework relies on `MessageConverters`, -since such requests already converted to Spring `Message`. In other words, the framework locates and applies the appropriate `MessageConverter`. -To accomplish that, the framework needs some instructions from the user. One of these instructions is already provided by the signature of the function -itself (Person type). Consequently, in theory, that should be (and, in some cases, is) enough. However, for the majority of use cases, in order to -select the appropriate `MessageConverter`, the framework needs an additional piece of information. That missing piece is `contentType` header. - -Such header usually comes as part of the Message where it is injected by the corresponding adapter that created such Message in the first place. -For example, HTTP POST request will have its content-type HTTP header copied to `contentType` header of the Message. - -For cases when such header does not exist framework relies on the default content type as `application/json`. - - -==== Content Type versus Argument Type - -As mentioned earlier, for the framework to select the appropriate `MessageConverter`, it requires argument type and, optionally, content type information. -The logic for selecting the appropriate `MessageConverter` resides with the argument resolvers which trigger right before the invocation of the user-defined -function (which is when the actual argument type is known to the framework). -If the argument type does not match the type of the current payload, the framework delegates to the stack of the -pre-configured `MessageConverters` to see if any one of them can convert the payload. - -The combination of `contentType` and argument type is the mechanism by which framework determines if message can be converted to a target type by locating -the appropriate `MessageConverter`. -If no appropriate `MessageConverter` is found, an exception is thrown, which you can handle by adding a custom `MessageConverter` -(see `<>`). - -NOTE: Do not expect `Message` to be converted into some other type based only on the `contentType`. -Remember that the `contentType` is complementary to the target type. -It is a hint, which `MessageConverter` may or may not take into consideration. - -==== Message Converters - -`MessageConverters` define two methods: - -[source, java] ----- -Object fromMessage(Message message, Class targetClass); - -Message toMessage(Object payload, @Nullable MessageHeaders headers); ----- - -It is important to understand the contract of these methods and their usage, specifically in the context of Spring Cloud Stream. - -The `fromMessage` method converts an incoming `Message` to an argument type. -The payload of the `Message` could be any type, and it is -up to the actual implementation of the `MessageConverter` to support multiple types. - - -==== Provided MessageConverters - -As mentioned earlier, the framework already provides a stack of `MessageConverters` to handle most common use cases. -The following list describes the provided `MessageConverters`, in order of precedence (the first `MessageConverter` that works is used): - -. `JsonMessageConverter`: Supports conversion of the payload of the `Message` to/from POJO for cases when `contentType` is `application/json` using Jackson (DEFAULT) or Gson libraries. This message converter also aware of `type` parameter (e.g., _application/json;type=foo.bar.Person_). This is useful for cases where types may not be known at the time when function is developed, hence function signature may look like `Function` or `Function` or `Function`. In other words for type conversion we typically derive type from function signature. Having, mime-type parameter allows you to communicate type in a more dynamic way. -. `ByteArrayMessageConverter`: Supports conversion of the payload of the `Message` from `byte[]` to `byte[]` for cases when `contentType` is `application/octet-stream`. It is essentially a pass through and exists primarily for backward compatibility. -. `StringMessageConverter`: Supports conversion of any type to a `String` when `contentType` is `text/plain`. - -When no appropriate converter is found, the framework throws an exception. When that happens, you should check your code and configuration and ensure you did -not miss anything (that is, ensure that you provided a `contentType` by using a binding or a header). -However, most likely, you found some uncommon case (such as a custom `contentType` perhaps) and the current stack of provided `MessageConverters` -does not know how to convert. If that is the case, you can add custom `MessageConverter`. See <>. - -[[user-defined-message-converters]] -==== User-defined Message Converters - -Spring Cloud Function exposes a mechanism to define and register additional `MessageConverters`. -To use it, implement `org.springframework.messaging.converter.MessageConverter`, configure it as a `@Bean`. -It is then appended to the existing stack of `MessageConverter`s. - -NOTE: It is important to understand that custom `MessageConverter` implementations are added to the head of the existing stack. -Consequently, custom `MessageConverter` implementations take precedence over the existing ones, which lets you override as well as add to the existing converters. - -The following example shows how to create a message converter bean to support a new content type called `application/bar`: - -[source,java] ----- -@SpringBootApplication -public static class SinkApplication { - - ... - - @Bean - public MessageConverter customMessageConverter() { - return new MyCustomMessageConverter(); - } -} - -public class MyCustomMessageConverter extends AbstractMessageConverter { - - public MyCustomMessageConverter() { - super(new MimeType("application", "bar")); - } - - @Override - protected boolean supports(Class clazz) { - return (Bar.class.equals(clazz)); - } - - @Override - protected Object convertFromInternal(Message message, Class targetClass, Object conversionHint) { - Object payload = message.getPayload(); - return (payload instanceof Bar ? payload : new Bar((byte[]) payload)); - } -} ----- - -==== Note on JSON options - -In Spring Cloud Function we support Jackson and Gson mechanisms to deal with JSON. -And for your benefit have abstracted it under `org.springframework.cloud.function.json.JsonMapper` which itself is aware of two mechanisms and will use the one selected -by you or following the default rule. -The default rules are as follows: - -* Whichever library is on the classpath that is the mechanism that is going to be used. So if you have `com.fasterxml.jackson.*` to the classpath, Jackson is going to be used and if you have `com.google.code.gson`, then Gson will be used. -* If you have both, then Gson will be the default, or you can set `spring.cloud.function.preferred-json-mapper` property with either of two values: `gson` or `jackson`. - - -That said, the type conversion is usually transparent to the developer, however given that `org.springframework.cloud.function.json.JsonMapper` is also registered as a bean -you can easily inject it into your code if needed. - - -=== Kotlin Lambda support - -We also provide support for Kotlin lambdas (since v2.0). -Consider the following: - -[source, java] ----- -@Bean -open fun kotlinSupplier(): () -> String { - return { "Hello from Kotlin" } -} - -@Bean -open fun kotlinFunction(): (String) -> String { - return { it.toUpperCase() } -} - -@Bean -open fun kotlinConsumer(): (String) -> Unit { - return { println(it) } -} - ----- -The above represents Kotlin lambdas configured as Spring beans. The signature of each maps to a Java equivalent of -`Supplier`, `Function` and `Consumer`, and thus supported/recognized signatures by the framework. -While mechanics of Kotlin-to-Java mapping are outside of the scope of this documentation, it is important to understand that the -same rules for signature transformation outlined in "Java 8 function support" section are applied here as well. - -To enable Kotlin support all you need is to add Kotlin SDK libraries on the classpath which will trigger appropriate -autoconfiguration and supporting classes. - -=== Function Component Scan - -Spring Cloud Function will scan for implementations of `Function`, `Consumer` and `Supplier` in a package called `functions` if it exists. Using this -feature you can write functions that have no dependencies on Spring - not even the `@Component` annotation is needed. If you want to use a different -package, you can set `spring.cloud.function.scan.packages`. You can also use `spring.cloud.function.scan.enabled=false` to switch off the scan completely. - -== Standalone Web Applications - -Functions could be automatically exported as HTTP endpoints. - -The `spring-cloud-function-web` module has autoconfiguration that -activates when it is included in a Spring Boot web application (with -MVC support). There is also a `spring-cloud-starter-function-web` to -collect all the optional dependencies in case you just want a simple -getting started experience. - -With the web configurations activated your app will have an MVC -endpoint (on "/" by default, but configurable with -`spring.cloud.function.web.path`) that can be used to access the -functions in the application context where function name becomes part of the URL path. The supported content types are -plain text and JSON. - -IMPORTANT: It is important to understand that while SCF provides ability to export Functional beans as REST endpoints it is NOT a replacement for Spring MVC/WebFlux etc. -It is primarily to accommodate _stateless serverless patterns_ where you simply want to have some stateless functionality to be exposed via HTTP. - -|=== -| Method | Path | Request | Response | Status - -| GET | /{supplier} | - | Items from the named supplier | 200 OK -| POST | /{consumer} | JSON object or text | Mirrors input and pushes request body into consumer | 202 Accepted -| PUT | /{consumer} | JSON object or text | Mirrors input and pushes request body into consumer | 202 Accepted -| DELETE | /{consumer} | JSON object or text | - | 204 NO CONTENT -| POST | /{function} | JSON object or text | The result of applying the named function | 200 OK -| PUT | /{function} | JSON object or text | The result of applying the named function | 200 OK -| GET | /{function}/{item} | - | Convert the item into an object and return the result of applying the function | 200 OK - -|=== - -As the table above shows the behavior of the endpoint depends on the method and also the type of incoming request data. When the incoming data is single valued, and the target function is declared as obviously single valued (i.e. not returning a collection or `Flux`), then the response will also contain a single value. -For multi-valued responses the client can ask for a server-sent event stream by sending `Accept: text/event-stream`. - -Functions and consumers that are declared with input and output in `Message` will see the request headers as _message headers_, and the output _message headers_ will be converted to HTTP headers. -The _payload_ of the Message will be a `body` or empty string if there is no `body` or it is null. - -When POSTing text the response format might be different with Spring Boot 2.0 and older versions, depending on the content negotiation (provide content type and accept headers for the best results). - -See <> to see the details and example on how to test such application. - -==== HTTP Request Parameters -As you have noticed from the previous table, you can pass an argument to a function as path variable (i.e., `/{function}/{item}`). -For example, `http://localhost:8080/uppercase/foo` will result in calling `uppercase` function with its input parameter being `foo`. - -While this is the recommended approach and the one that fits most use cases cases, there are times when you have to deal with HTTP request parameters (e.g., `http://localhost:8080/uppercase/foo?name=Bill`) -The framework will treat HTTP request parameters similar to the HTTP headers by storing them in the `Message` headers under the header key `http_request_param` -with its value being a `Map` of request parameters, so in order to access them your function input signature should accept `Message` type (e.g., `Function, String>`). For convenience we provide `HeaderUtils.HTTP_REQUEST_PARAM` constant. - -=== Function Mapping rules - -If there is only a single function (consumer etc.) in the catalog, the name in the path is optional. -In other words, providing you only have `uppercase` function in catalog -`curl -H "Content-Type: text/plain" localhost:8080/uppercase -d hello` and `curl -H "Content-Type: text/plain" localhost:8080/ -d hello` calls are identical. - -Composite functions can be addressed using pipes or commas to separate function names (pipes are legal in URL paths, but a bit awkward to type on the command line). -For example, `curl -H "Content-Type: text/plain" localhost:8080/uppercase,reverse -d hello`. - -For cases where there is more than a single function in catalog, each function will be exported and mapped with function name being -part of the path (e.g., `localhost:8080/uppercase`). -In this scenario you can still map specific function or function composition to the root path by providing -`spring.cloud.function.definition` property - -For example, ----- ---spring.cloud.function.definition=foo|bar ----- - -The above property will compose 'foo' and 'bar' function and map the composed function to the "/" path. - -The same property will also work for cases where function can not be resolved via URL. For example, your URL may be `localhost:8080/uppercase`, but there is no `uppercase` function. -However there are function `foo` and `bar`. So, in this case `localhost:8080/uppercase` will resolve to `foo|bar`. -This could be useful especially for cases when URL is used to communicate certain information since there will be Message header called `uri` with the value -of the actual URL, giving user ability to use it for evaluation and computation. - -=== Function Filtering rules - -In situations where there are more than one function in catalog there may be a need to only export certain functions or function compositions. In that case you can use -the same `spring.cloud.function.definition` property listing functions you intend to export delimited by `;`. -Note that in this case nothing will be mapped to the root path and functions that are not listed (including compositions) are not going to be exported - -For example, - ----- ---spring.cloud.function.definition=foo;bar ----- - -This will only export function `foo` and function `bar` regardless how many functions are available in catalog (e.g., `localhost:8080/foo`). - ----- ---spring.cloud.function.definition=foo|bar;baz ----- - -This will only export function composition `foo|bar` and function `baz` regardless how many functions are available in catalog (e.g., `localhost:8080/foo,bar`). - -=== CRUD REST with Spring Cloud Function - -By now it should be clear that functions are exported as REST endpoints and can be invoked using various HTTP methods. In other words a single -function could be triggered via GET, POST, PUT etc. - -However, it is not always desirable and certainly does not fit the CRUD concept. And while SCF does not support and has no intention of supporting -all the features of Spring web stack, the framework does provide support for CRUD mappings where a single function could be mapped to a particular HTTP method(s). -It is done via spring.cloud.function.http. property. - -For example, - ----- -spring.cloud.function.http.GET=uppercase;reverse;foo|bar -spring.cloud.function.http.POST=reverse -spring.cloud.function.http.DELETE=deleteById ----- - -As you can see, here we’re mapping functions to various HTTP methods using the same rules as `spring.cloud.function.definition` property where “;” allows us to define several functions and “|” signifies function composition. - -== Standalone Streaming Applications - -To send or receive messages from a broker (such as RabbitMQ or Kafka) you can leverage `spring-cloud-stream` project and it's integration with Spring Cloud Function. -Please refer to https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/current/reference/html/spring-cloud-stream.html#spring_cloud_function[Spring Cloud Function] section of the https://spring.io/projects/spring-cloud-stream[Spring Cloud Stream] reference manual for more details and examples. - -== Deploying a Packaged Function - -Spring Cloud Function provides a "deployer" library that allows you to launch a jar file (or exploded archive, or set of jar files) with an isolated class loader and expose the functions defined in it. This is quite a powerful tool that would allow you to, for instance, adapt a function to a range of different input-output adapters without changing the target jar file. Serverless platforms often have this kind of feature built in, so you could see it as a building block for a function invoker in such a platform (indeed the https://projectriff.io[Riff] Java function invoker uses this library). - -The standard entry point is to add `spring-cloud-function-deployer` to the classpath, the deployer kicks in and looks for some configuration to tell it where to find the function jar. - -```xml - - org.springframework.cloud - spring-cloud-function-deployer - ${spring.cloud.function.version} - -``` - - -At a minimum the user has to provide a `spring.cloud.function.location` which is a URL or resource location for the archive containing -the functions. It can optionally use a `maven:` prefix to locate the artifact via a dependency lookup (see `FunctionProperties` -for complete details). A Spring Boot application is bootstrapped from the jar file, using the `MANIFEST.MF` to locate a start class, so -that a standard Spring Boot fat jar works well, for example. If the target jar can be launched successfully then the result is a function -registered in the main application's `FunctionCatalog`. The registered function can be applied by code in the main application, even though -it was created in an isolated class loader (by deault). - -Here is the example of deploying a JAR which contains an 'uppercase' function and invoking it . - -```java -@SpringBootApplication -public class DeployFunctionDemo { - - public static void main(String[] args) { - ApplicationContext context = SpringApplication.run(DeployFunctionDemo.class, - "--spring.cloud.function.location=..../target/uppercase-0.0.1-SNAPSHOT.jar", - "--spring.cloud.function.definition=uppercase"); - - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function function = catalog.lookup("uppercase"); - System.out.println(function.apply("hello")); - } -} -``` - -And here is the example using Maven URI (taken from one of the tests in `FunctionDeployerTests`): - -```java -@SpringBootApplication -public class DeployFunctionDemo { - - public static void main(String[] args) { - String[] args = new String[] { - "--spring.cloud.function.location=maven://oz.demo:demo-uppercase:0.0.1-SNAPSHOT", - "--spring.cloud.function.function-class=oz.demo.uppercase.MyFunction" }; - - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function function = catalog.lookup("myFunction"); - - assertThat(function.apply("bob")).isEqualTo("BOB"); - } -} -``` - -Keep in mind that Maven resource such as local and remote repositories, user, password and more are resolved using default MavenProperties which -effectively use local defaults and will work for majority of cases. However if you need to customize you can simply provide a bean of type -`MavenProperties` where you can set additional properties (see example below). - -```java -@Bean -public MavenProperties mavenProperties() { - MavenProperties properties = new MavenProperties(); - properties.setLocalRepository("target/it/"); - return properties; -} -``` - -=== Supported Packaging Scenarios - -Currently Spring Cloud Function supports several packaging scenarios to give you the most flexibility when it comes to deploying functions. - -==== Simple JAR - -This packaging option implies no dependency on anything related to Spring. -For example; Consider that such JAR contains the following class: -```java -package function.example; -. . . -public class UpperCaseFunction implements Function { - @Override - public String apply(String value) { - return value.toUpperCase(); - } -} -``` -All you need to do is specify `location` and `function-class` properties when deploying such package: - -``` ---spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar ---spring.cloud.function.function-class=function.example.UpperCaseFunction -``` - -It's conceivable in some cases that you might want to package multiple functions together. For such scenarios you can use -`spring.cloud.function.function-class` property to list several classes delimiting them by `;`. - -For example, - -``` ---spring.cloud.function.function-class=function.example.UpperCaseFunction;function.example.ReverseFunction -``` - -Here we are identifying two functions to deploy, which we can now access in function catalog by name (e.g., `catalog.lookup("reverseFunction");`). - - -For more details please reference the complete sample available https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-deployer/src/it/simplestjar[here]. -You can also find a corresponding test in https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java#L70[FunctionDeployerTests]. - -*** Component Scanning *** - -Since version 3.1.4 you can simplify your configuration thru component scanning feature described in <>. If you place your functional class in -package named `functions`, you can omit `spring.cloud.function.function-class` property as framework will auto-discover functional classes loading them in function catalog. -Keep in mind the naming convention to follow when doing function lookup. For example function class `functions.UpperCaseFunction` will be available in `FunctionCatalog` -under the name `upperCaseFunction`. - -==== Spring Boot JAR - -This packaging option implies there is a dependency on Spring Boot and that the JAR was generated as Spring Boot JAR. That said, given that the deployed JAR -runs in the isolated class loader, there will not be any version conflict with the Spring Boot version used by the actual deployer. -For example; Consider that such JAR contains the following class (which could have some additional Spring dependencies providing Spring/Spring Boot is on the classpath): -```java -package function.example; -. . . -public class UpperCaseFunction implements Function { - @Override - public String apply(String value) { - return value.toUpperCase(); - } -} -``` -As before all you need to do is specify `location` and `function-class` properties when deploying such package: - -``` ---spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar ---spring.cloud.function.function-class=function.example.UpperCaseFunction -``` -For more details please reference the complete sample available https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-deployer/src/it/bootjar[here]. -You can also find a corresponding test in https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java#L50[FunctionDeployerTests]. - -==== Spring Boot Application - -This packaging option implies your JAR is complete stand alone Spring Boot application with functions as managed Spring beans. -As before there is an obvious assumption that there is a dependency on Spring Boot and that the JAR was generated as Spring Boot JAR. That said, given that the deployed JAR -runs in the isolated class loader, there will not be any version conflict with the Spring Boot version used by the actual deployer. -For example; Consider that such JAR contains the following class: -```java -package function.example; -. . . -@SpringBootApplication -public class SimpleFunctionAppApplication { - - public static void main(String[] args) { - SpringApplication.run(SimpleFunctionAppApplication.class, args); - } - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } -} -``` -Given that we're effectively dealing with another Spring Application context and that functions are spring managed beans, -in addition to the `location` property we also specify `definition` property instead of `function-class`. - -``` ---spring.cloud.function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar ---spring.cloud.function.definition=uppercase -``` -For more details please reference the complete sample available https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-deployer/src/it/bootapp[here]. -You can also find a corresponding test in https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java#L164[FunctionDeployerTests]. - -NOTE: This particular deployment option may or may not have Spring Cloud Function on it's classpath. From the deployer perspective this doesn't matter. - -== Functional Bean Definitions - -include::functional.adoc[leveloffset=+1] - -== Serverless Platform Adapters - -As well as being able to run as a standalone process, a Spring Cloud -Function application can be adapted to run one of the existing -serverless platforms. In the project there are adapters for -https://github.com/spring-cloud/spring-cloud-function/tree/{branch}/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS -Lambda], and https://github.com/spring-cloud/spring-cloud-function/tree/{branch}/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Azure]. The https://github.com/fnproject/fn[Oracle Fn platform] has its own Spring Cloud Function adapter. And https://projectriff.io[Riff] supports Java functions and its -https://github.com/projectriff/java-function-invoker[Java Function Invoker] acts natively is an adapter for Spring Cloud Function jars. - -include::adapters/aws-intro.adoc[] -include::adapters/azure-intro.adoc[leveloffset=+1] -include::adapters/gcp-intro.adoc[] - diff --git a/docs/src/main/asciidoc/spring-integration.adoc b/docs/src/main/asciidoc/spring-integration.adoc deleted file mode 100644 index 52c6d1499..000000000 --- a/docs/src/main/asciidoc/spring-integration.adoc +++ /dev/null @@ -1,101 +0,0 @@ -[[spring-integration]] -== Spring Integration Interaction - -https://spring.io/projects/spring-integration[Spring Integration Framework] extends the Spring programming model to support the well-known Enterprise Integration Patterns. -It enables lightweight messaging within Spring-based applications and supports integration with external systems via declarative adapters. -It also provides a high-level DSL to compose various operations (endpoints) into a logical integration flow. -With a lambda style of this DSL configuration, Spring Integration already has a good level of `java.util.function` interfaces adoption. -The `@MessagingGateway` proxy interface can also be as a `Function` or `Consumer`, which according to the Spring Cloud Function environment can be registered into a function catalog. -See more information in Spring Integration https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#functions-support[ReferenceManual] about its support for functions. - -On the other hand, starting with version `4.0.3`, Spring Cloud Function introduces a `spring-cloud-function-integration` module which provides deeper, more cloud-specific and auto-configuration based API for interaction with a `FunctionCatalog` from Spring Integration DSL perspective. -The `FunctionFlowBuilder` is auto-configured and autowired with a `FunctionCatalog` and represents an entry point for function-specific DSL for target `IntegrationFlow` instance. -In addition to standard `IntegrationFlow.from()` factories (for convenience), the `FunctionFlowBuilder` exposes a `fromSupplier(String supplierDefinition)` factory to lookup the target `Supplier` in the provided `FunctionCatalog`. -Then this `FunctionFlowBuilder` leads to the `FunctionFlowDefinition`. -This `FunctionFlowDefinition` is an implementation of the `IntegrationFlowExtension` and exposes `apply(String functionDefinition)` and `accept(String consumerDefinition)` operators to lookup `Function` or `Consumer` from the `FunctionCatalog`, respectively. -See their Javadocs for more information. - -The following example demonstrates the `FunctionFlowBuilder` in action alongside with the power of the rest of `IntegrationFlow` API: - -[source,java] ----- -@Configuration -public class IntegrationConfiguration { - - @Bean - Supplier simpleByteArraySupplier() { - return "simple test data"::getBytes; - } - - @Bean - Function upperCaseFunction() { - return String::toUpperCase; - } - - @Bean - BlockingQueue results() { - return new LinkedBlockingQueue<>(); - } - - @Bean - Consumer simpleStringConsumer(BlockingQueue results) { - return results::add; - } - - @Bean - QueueChannel wireTapChannel() { - return new QueueChannel(); - } - - @Bean - IntegrationFlow someFunctionFlow(FunctionFlowBuilder functionFlowBuilder) { - return functionFlowBuilder - .fromSupplier("simpleByteArraySupplier") - .wireTap("wireTapChannel") - .apply("upperCaseFunction") - .log(LoggingHandler.Level.WARN) - .accept("simpleStringConsumer"); - } - -} ----- - -Since the `FunctionCatalog.lookup()` functionality is not limited just to simple function names, a function composition feature can also be used in the mentioned `apply()` and `accept()` operators: - -[source,java] ----- -@Bean -IntegrationFlow functionCompositionFlow(FunctionFlowBuilder functionFlowBuilder) { - return functionFlowBuilder - .from("functionCompositionInput") - .accept("upperCaseFunction|simpleStringConsumer"); -} ----- - -This API becomes more relevant, when we add into our Spring Cloud applications auto-configuration dependencies for predefined functions. -For example https://spring.io/projects/spring-cloud-stream-applications[Stream Applications] project, in addition to application images, provides artifacts with functions for various integration use-case, e.g. `debezium-supplier`, `elasticsearch-consumer`, `aggregator-function` etc. - -The following configuration is based on the `http-supplier`, `spel-function` and `file-consumer`, respectively: - -[source,java] ----- -@Bean -IntegrationFlow someFunctionFlow(FunctionFlowBuilder functionFlowBuilder) { - return functionFlowBuilder - .fromSupplier("httpSupplier", e -> e.poller(Pollers.trigger(new OnlyOnceTrigger()))) - .>handle((fluxPayload, headers) -> fluxPayload, e -> e.async(true)) - .channel(c -> c.flux()) - .apply("spelFunction") - .transform(String::toUpperCase) - .accept("fileConsumer"); -} ----- - -What we would need else is just to add their configuration into an `application.properties` (if necessary): - -[source,properties] ----- -http.path-pattern=/testPath -spel.function.expression=new String(payload) -file.consumer.name=test-data.txt ----- diff --git a/docs/src/main/images/ce.png b/docs/src/main/images/ce.png deleted file mode 100644 index e090018e7..000000000 Binary files a/docs/src/main/images/ce.png and /dev/null differ diff --git a/docs/src/main/images/message.png b/docs/src/main/images/message.png deleted file mode 100644 index e33bcd586..000000000 Binary files a/docs/src/main/images/message.png and /dev/null differ diff --git a/docs/src/main/ruby/generate_readme.sh b/docs/src/main/ruby/generate_readme.sh deleted file mode 100755 index 6d0ce9dc5..000000000 --- a/docs/src/main/ruby/generate_readme.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env ruby - -base_dir = File.join(File.dirname(__FILE__),'../../..') -src_dir = File.join(base_dir, "/src/main/asciidoc") -require 'asciidoctor' -require 'optparse' - -options = {} -file = "#{src_dir}/README.adoc" - -OptionParser.new do |o| - o.on('-o OUTPUT_FILE', 'Output file (default is stdout)') { |file| options[:to_file] = file unless file=='-' } - o.on('-h', '--help') { puts o; exit } - o.parse! -end - -file = ARGV[0] if ARGV.length>0 - -# Copied from https://github.com/asciidoctor/asciidoctor-extensions-lab/blob/master/scripts/asciidoc-coalescer.rb -doc = Asciidoctor.load_file file, safe: :unsafe, header_only: true, attributes: options[:attributes] -header_attr_names = (doc.instance_variable_get :@attributes_modified).to_a -header_attr_names.each {|k| doc.attributes[%(#{k}!)] = '' unless doc.attr? k } -attrs = doc.attributes -attrs['allow-uri-read'] = true -puts attrs - -out = "// Do not edit this file (e.g. go instead to src/main/asciidoc)\n\n" -doc = Asciidoctor.load_file file, safe: :unsafe, parse: false, attributes: attrs -out << doc.reader.read - -unless options[:to_file] - puts out -else - File.open(options[:to_file],'w+') do |file| - file.write(out) - end -end diff --git a/package.json b/package.json new file mode 100644 index 000000000..c3570e2f8 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "dependencies": { + "antora": "3.2.0-alpha.4", + "@antora/atlas-extension": "1.0.0-alpha.2", + "@antora/collector-extension": "1.0.0-alpha.3", + "@asciidoctor/tabs": "1.0.0-beta.6", + "@springio/antora-extensions": "1.11.1", + "@springio/asciidoctor-extensions": "1.0.0-alpha.10" + } +} diff --git a/pipeline.yml b/pipeline.yml deleted file mode 100644 index fbf8f9e54..000000000 --- a/pipeline.yml +++ /dev/null @@ -1,76 +0,0 @@ -# fly --target cloud login --concourse-url https://ci.spring.io --team-name spring-cloud -# fly --target cloud set-pipeline --config pipeline.yml --pipeline spring-cloud-function --load-vars-from credentials.yml ---- -jobs: - - name: build - plan: - - get: source - trigger: true - - task: maven - config: - platform: linux - image_resource: - type: docker-image - source: - repository: springio/maven-base - inputs: - - name: source - caches: - - path: source/.m2 - run: - dir: source - path: sh - args: - - -c - - | - rm -rf ~/.m2 - ln -s $(pwd)/.m2 ~/.m2 - ./mvnw deploy -s .mvn/settings.xml -Dgpg.passphrase="${passphrase}" - params: - TERM: -dumb - passphrase: {{passphrase}} - sonatype_username: {{sonatype-username}} - sonatype_password: {{sonatype-password}} - spring_username: {{spring-username}} - spring_password: {{spring-password}} - on_failure: *slack-failure - on_success: *slack-success - -slack-failure: &slack-failure - put: slack - params: - channel: spring-cloud-firehose - attachments: - - color: danger - fallback: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME " - text: "Build has failed" - title: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME" - title_link: $ATC_EXTERNAL_URL/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME - -slack-success: &slack-success - put: slack - params: - channel: spring-cloud-firehose - attachments: - - color: good - fallback: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME " - text: "Build has succeeded" - title: "$BUILD_PIPELINE_NAME/$BUILD_JOB_NAME #$BUILD_NAME" - title_link: $ATC_EXTERNAL_URL/teams/$BUILD_TEAM_NAME/pipelines/$BUILD_PIPELINE_NAME/jobs/$BUILD_JOB_NAME/builds/$BUILD_NAME - -resources: - - name: source - type: git - source: - uri: https://github.com/spring-cloud/spring-cloud-function.git - - name: slack - type: slack-notification - source: - url: {{slack-url}} - -resource_types: - - name: slack-notification - type: docker-image - source: - repository: nebhale/slack-notification-resource - diff --git a/pom.xml b/pom.xml index bdf655a3a..7320f5031 100644 --- a/pom.xml +++ b/pom.xml @@ -1,177 +1,43 @@ - - - 4.0.0 + + - spring-cloud-function-parent - Spring Cloud Function Parent - 4.1.0-SNAPSHOT - pom + 4.0.0 + spring-cloud-function-docs-build + ${parent.version} org.springframework.cloud - spring-cloud-build - 4.1.0-SNAPSHOT - + spring-cloud-function-parent + 4.1.4-SNAPSHOT - - 17 - ${java.version} - ${java.version} - 1.0.27.RELEASE - spring-cloud-function - true - true - - true - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${project.version} - pom - import - - - + Spring Cloud Function Docs Build + Builds Spring Cloud Function Docs. + https://spring.io/projects/spring-cloud-function + + scm:git:https://github.com/spring-cloud/spring-cloud-function.git + + + scm:git:git@github.com:spring-cloud/spring-cloud-function.git + + https://github.com/spring-cloud/spring-cloud-function + + + https://github.com/spring-cloud/spring-cloud-function/issues + - - org.apache.maven.plugins - maven-checkstyle-plugin + org.antora + antora-maven-plugin - - org.apache.maven.plugins - maven-surefire-plugin - - --add-opens java.base/java.util=ALL-UNNAMED - - - - - - - org.codehaus.mojo - flatten-maven-plugin - - - maven-javadoc-plugin - false - - true - com.example,functions,example - - - - - aggregate - - aggregate-jar - - package - - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring-boot.version} - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.codehaus.mojo - - - flatten-maven-plugin - - - [1.0.0,) - - - flatten - - - - - - - - - - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - - - - - - core - - spring-cloud-function-dependencies - spring-cloud-function-core - spring-cloud-function-context - spring-cloud-function-web - - - - all - - true - - - spring-cloud-function-dependencies - spring-cloud-function-core - spring-cloud-function-context - spring-cloud-function-web - spring-cloud-starter-function-web - spring-cloud-starter-function-webflux - spring-cloud-function-samples - spring-cloud-function-deployer - spring-cloud-function-adapters - spring-cloud-function-integration - spring-cloud-function-rsocket - spring-cloud-function-kotlin - docs - - - - spring-snapshots - Spring Snapshots + spring-snapshot https://repo.spring.io/snapshot true @@ -181,49 +47,8 @@ - spring-milestones - Spring Milestones + spring-milestone https://repo.spring.io/milestone - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - diff --git a/spring-cloud-function-adapters/pom.xml b/spring-cloud-function-adapters/pom.xml deleted file mode 100644 index cc9d1fee1..000000000 --- a/spring-cloud-function-adapters/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-adapter-parent - pom - - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - - spring-cloud-function-adapter-parent - - - spring-cloud-function-adapter-aws - - spring-cloud-function-adapter-azure - spring-cloud-function-adapter-gcp - spring-cloud-function-grpc - spring-cloud-function-grpc-cloudevent-ext - spring-cloud-function-serverless-web - spring-cloud-function-adapter-azure-web - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/README.adoc b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/README.adoc deleted file mode 100644 index 5414034e8..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/README.adoc +++ /dev/null @@ -1,7 +0,0 @@ -//// -DO NOT EDIT THIS FILE. IT WAS GENERATED. -Manual changes to this file will be lost when it is generated again. -Edit the files in the src/main/asciidoc/ directory instead. -//// - -This project provides an adapter layer for a Spring Cloud Function application onto AWS Lambda. You can write an app with a single `@Bean` of type `Function`, `Consumer` or `Supplier` and it will be deployable in AWS if you get the JAR file laid out right. The best way to make it work is to include `spring-cloud-function-context` as a dependency, but not the higher level adapters (e.g. `spring-cloud-function-stream`). diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml deleted file mode 100644 index 0399000f8..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml +++ /dev/null @@ -1,162 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-adapter-aws - jar - - spring-cloud-function-adapter-aws - AWS Lambda Adapter for Spring Cloud Function - - - org.springframework.cloud - spring-cloud-function-adapter-parent - 4.1.0-SNAPSHOT - - - - UTF-8 - UTF-8 - 3.9.0 - 1.12.29 - 1.0.3 - 1.0.3 - - - - - org.springframework.cloud - spring-cloud-function-context - - - org.springframework.boot - spring-boot-starter - - - com.amazonaws - aws-lambda-java-log4j - 1.0.0 - provided - - - com.amazonaws - aws-lambda-java-serialization - 1.0.0 - provided - - - com.amazonaws - aws-lambda-java-core - 1.2.1 - provided - - - com.amazonaws - amazon-kinesis-client - 1.14.4 - provided - - - com.amazonaws - aws-lambda-java-events - ${aws-lambda-events.version} - provided - - - com.amazonaws - aws-java-sdk-kinesis - ${aws-java-sdk.version} - provided - true - - - com.amazonaws - amazon-kinesis-deaggregator - ${aws-kinesis-deaggregator.version} - - - com.amazonaws - aws-lambda-java-events - - - true - provided - - - - - - - - org.springframework - spring-webmvc - - - org.springframework.cloud - spring-cloud-function-web - true - - - org.springframework.boot - spring-boot-starter-test - test - - - com.amazonaws - amazon-kinesis-aggregator - ${aws-kinesis-aggregator.version} - test - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-web - test - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSCompanionAutoConfiguration.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSCompanionAutoConfiguration.java deleted file mode 100644 index de6ac6721..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSCompanionAutoConfiguration.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.springframework.cloud.function.json.JacksonMapper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.util.CollectionUtils; - - -/** - * - * @author Oleg Zhurakousky - * @since 3.2 - * - */ -public class AWSCompanionAutoConfiguration { - - @Bean - public AWSTypesMessageConverter awsTypesMessageConverter(GenericApplicationContext applicationContext) { - JsonMapper jsonMapper = CollectionUtils.isEmpty(applicationContext.getBeansOfType(JsonMapper.class).values()) - ? new JacksonMapper(new ObjectMapper()) - : applicationContext.getBean(JsonMapper.class); - return new AWSTypesMessageConverter(jsonMapper); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java deleted file mode 100644 index 0923e234b..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; - -import com.amazonaws.services.lambda.runtime.Context; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; - -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.http.HttpStatus; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.StreamUtils; - -/** - * - * @author Oleg Zhurakousky - * - */ -public final class AWSLambdaUtils { - - private static Log logger = LogFactory.getLog(AWSLambdaUtils.class); - - static final String AWS_API_GATEWAY = "aws-api-gateway"; - - static final String AWS_EVENT = "aws-event"; - - static final String IS_BASE64_ENCODED = "isBase64Encoded"; - - static final String STATUS_CODE = "statusCode"; - - static final String BODY = "body"; - - static final String HEADERS = "headers"; - - /** - * The name of the headers that stores AWS Context object. - */ - public static final String AWS_CONTEXT = "aws-context"; - - private AWSLambdaUtils() { - - } - - static boolean isSupportedAWSType(Type inputType) { - if (FunctionTypeUtils.isMessage(inputType) || FunctionTypeUtils.isPublisher(inputType)) { - inputType = FunctionTypeUtils.getImmediateGenericType(inputType, 0); - } - return FunctionTypeUtils.getRawType(inputType).getPackage().getName().startsWith("com.amazonaws.services.lambda.runtime.events"); - } - - @SuppressWarnings("rawtypes") - public static Message generateMessage(InputStream payload, Type inputType, boolean isSupplier, JsonMapper jsonMapper, Context context) throws IOException { - if (inputType != null && FunctionTypeUtils.isMessage(inputType)) { - inputType = FunctionTypeUtils.getImmediateGenericType(inputType, 0); - } - if (inputType != null && InputStream.class.isAssignableFrom(FunctionTypeUtils.getRawType(inputType))) { - MessageBuilder msgBuilder = MessageBuilder.withPayload(payload); - if (context != null) { - msgBuilder.setHeader(AWSLambdaUtils.AWS_CONTEXT, context); - } - return msgBuilder.build(); - } - else { - return generateMessage(StreamUtils.copyToByteArray(payload), inputType, isSupplier, jsonMapper, context); - } - } - - public static Message generateMessage(byte[] payload, Type inputType, boolean isSupplier, JsonMapper jsonMapper) { - return generateMessage(payload, inputType, isSupplier, jsonMapper, null); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Message generateMessage(byte[] payload, Type inputType, boolean isSupplier, JsonMapper jsonMapper, Context context) { - if (logger.isInfoEnabled()) { - logger.info("Received: " + new String(payload, StandardCharsets.UTF_8)); - } - - - Object structMessage = jsonMapper.fromJson(payload, Object.class); - boolean isApiGateway = structMessage instanceof Map - && (((Map) structMessage).containsKey("httpMethod") || - (((Map) structMessage).containsKey("routeKey") && ((Map) structMessage).containsKey("version"))); - - Message requestMessage; - MessageBuilder builder = MessageBuilder.withPayload(payload); - if (isApiGateway) { - builder.setHeader(AWSLambdaUtils.AWS_API_GATEWAY, true); - if (JsonMapper.isJsonStringRepresentsCollection(((Map) structMessage).get("body"))) { - builder.setHeader("payload", ((Map) structMessage).get("body")); - } - } - if (!isSupplier && AWSLambdaUtils.isSupportedAWSType(inputType)) { - builder.setHeader(AWSLambdaUtils.AWS_EVENT, true); - } - if (context != null) { - builder.setHeader(AWSLambdaUtils.AWS_CONTEXT, context); - } - // - if (structMessage instanceof Map && ((Map) structMessage).containsKey("headers")) { - builder.copyHeaders((Map) ((Map) structMessage).get("headers")); - } - requestMessage = builder.build(); - return requestMessage; - } - - private static byte[] extractPayload(Message msg, JsonMapper objectMapper) { - if (msg.getPayload() instanceof byte[]) { - return (byte[]) msg.getPayload(); - } - else { - return objectMapper.toJson(msg.getPayload()); - } - } - - private static Object convertFromJsonIfNecessary(Object value, JsonMapper objectMapper) { - if (JsonMapper.isJsonString(value)) { - return objectMapper.fromJson(value, Object.class); - } - return value; - } - - @SuppressWarnings("unchecked") - public static byte[] generateOutputFromObject(Message requestMessage, Object output, JsonMapper objectMapper, Type functionOutputType) { - Message responseMessage = null; - if (output instanceof Publisher) { - List result = new ArrayList<>(); - Message lastMessage = null; - for (Object item : Flux.from((Publisher) output).toIterable()) { - if (logger.isDebugEnabled()) { - logger.debug("Response value: " + item); - } - if (item instanceof Message message) { - result.add(convertFromJsonIfNecessary(message.getPayload(), objectMapper)); - lastMessage = message; - } - else { - result.add(convertFromJsonIfNecessary(item, objectMapper)); - } - } - - byte[] resultPayload; - if (result.size() == 1) { - resultPayload = objectMapper.toJson(result.get(0)); - } - else if (result.size() > 1) { - resultPayload = objectMapper.toJson(result); - } - else { - resultPayload = null; - } - - if (resultPayload != null) { - MessageBuilder messageBuilder = MessageBuilder.withPayload(resultPayload); - if (lastMessage != null) { - messageBuilder.copyHeaders(lastMessage.getHeaders()); - } - responseMessage = messageBuilder.build(); - } - } - else { - responseMessage = (Message) output; - } - return generateOutput(requestMessage, responseMessage, objectMapper, functionOutputType); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static byte[] generateOutput(Message requestMessage, Message responseMessage, - JsonMapper objectMapper, Type functionOutputType) { - - Class outputClass = FunctionTypeUtils.getRawType(functionOutputType); - if (outputClass != null) { - String outputClassName = outputClass.getName(); - if (outputClassName.equals("com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse") || - outputClassName.equals("com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent") || - outputClassName.equals("com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerResponseEvent") || - outputClassName.equals("com.amazonaws.services.lambda.runtime.events.IamPolicyResponse")) { - return extractPayload((Message) responseMessage, objectMapper); - } - } - - byte[] responseBytes = responseMessage == null ? "\"OK\"".getBytes() : extractPayload((Message) responseMessage, objectMapper); - if (requestMessage.getHeaders().containsKey(AWS_API_GATEWAY) && ((boolean) requestMessage.getHeaders().get(AWS_API_GATEWAY))) { - Map response = new HashMap(); - response.put(IS_BASE64_ENCODED, responseMessage != null && responseMessage.getHeaders().containsKey(IS_BASE64_ENCODED) - ? responseMessage.getHeaders().get(IS_BASE64_ENCODED) : false); - - AtomicReference headers = new AtomicReference<>(); - int statusCode = HttpStatus.OK.value(); - if (responseMessage != null) { - headers.set(responseMessage.getHeaders()); - statusCode = headers.get().containsKey(STATUS_CODE) - ? (int) headers.get().get(STATUS_CODE) - : HttpStatus.OK.value(); - } - - response.put(STATUS_CODE, statusCode); - if (isRequestKinesis(requestMessage)) { - HttpStatus httpStatus = HttpStatus.valueOf(statusCode); - response.put("statusDescription", httpStatus.toString()); - } - - String body = responseMessage == null - ? "\"OK\"" : new String(extractPayload((Message) responseMessage, objectMapper), StandardCharsets.UTF_8); - response.put(BODY, body); - if (responseMessage != null) { - Map responseHeaders = new HashMap<>(); - headers.get().keySet().forEach(key -> responseHeaders.put(key, headers.get().get(key).toString())); - response.put(HEADERS, responseHeaders); - } - - try { - responseBytes = objectMapper.toJson(response); - } - catch (Exception e) { - throw new IllegalStateException("Failed to serialize AWS Lambda output", e); - } - } - return responseBytes; - } - - private static boolean isRequestKinesis(Message requestMessage) { - return requestMessage.getHeaders().containsKey("Records"); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSTypesMessageConverter.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSTypesMessageConverter.java deleted file mode 100644 index b94a48181..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSTypesMessageConverter.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; -import java.util.Map; - -import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; -import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; - -import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils; -import org.springframework.cloud.function.context.config.JsonMessageConverter; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.util.MimeType; - -/** - * Implementation of {@link MessageConverter} which uses Jackson or Gson libraries to do the - * actual conversion via {@link JsonMapper} instance. - * - * @author Oleg Zhurakousky - * - * @since 3.2 - */ -class AWSTypesMessageConverter extends JsonMessageConverter { - - private final JsonMapper jsonMapper; - - AWSTypesMessageConverter(JsonMapper jsonMapper) { - this(jsonMapper, new MimeType("application", "json"), new MimeType(CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getType(), - CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getSubtype() + "+json")); - } - - AWSTypesMessageConverter(JsonMapper jsonMapper, MimeType... supportedMimeTypes) { - super(jsonMapper, supportedMimeTypes); - this.jsonMapper = jsonMapper; - } - - @Override - protected boolean canConvertFrom(Message message, @Nullable Class targetClass) { - if (message.getHeaders().containsKey(AWSLambdaUtils.AWS_API_GATEWAY)) { - return ((boolean) message.getHeaders().get(AWSLambdaUtils.AWS_API_GATEWAY)); - } - if (message.getHeaders().containsKey(AWSLambdaUtils.AWS_EVENT)) { - return ((boolean) message.getHeaders().get(AWSLambdaUtils.AWS_EVENT)); - } - //TODO Do we really need the ^^ above? It seems like the line below dows the trick - else if (targetClass.getPackage().getName().startsWith("com.amazonaws.services.lambda.runtime.events")) { - return true; - } - return false; - } - - @Override - protected Object convertFromInternal(Message message, Class targetClass, @Nullable Object conversionHint) { - if (message.getPayload().getClass().isAssignableFrom(targetClass)) { - return message.getPayload(); - } - - if (targetClass.getPackage().getName().startsWith("com.amazonaws.services.lambda.runtime.events")) { - PojoSerializer serializer = LambdaEventSerializers.serializerFor(targetClass, Thread.currentThread().getContextClassLoader()); - Object event = serializer.fromJson(new ByteArrayInputStream((byte[]) message.getPayload())); - return event; - } - else { - Map structMessage = this.jsonMapper.fromJson(message.getPayload(), Map.class); - if (targetClass.isAssignableFrom(Map.class)) { - return structMessage; - } - else { - Object body; - if (message.getHeaders().containsKey("payload")) { - body = message.getPayload(); - } - else { - body = structMessage.get("body"); - } - Object convertedResult = this.jsonMapper.fromJson(body, targetClass); - return convertedResult; - } - } - } - - @Override - protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) { - if (!supportsMimeType(headers)) { - return false; - } - return true; - } - - - @Override - protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers, - @Nullable Object conversionHint) { - if (payload instanceof String && headers.containsKey(AWSLambdaUtils.IS_BASE64_ENCODED) && (boolean) headers.get(AWSLambdaUtils.IS_BASE64_ENCODED)) { - return ((String) payload).getBytes(StandardCharsets.UTF_8); - } - return jsonMapper.toJson(payload); - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java deleted file mode 100644 index 7e91f7eab..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright 2021-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import java.io.ByteArrayInputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.net.SocketException; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.text.MessageFormat; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - - -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.SmartLifecycle; -import org.springframework.core.env.Environment; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.messaging.Message; -import org.springframework.util.Assert; -import org.springframework.web.client.RestTemplate; - -import static org.apache.http.HttpHeaders.USER_AGENT; - -/** - * Event loop and necessary configurations to support AWS Lambda - * Custom Runtime - https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html. - * - * @author Oleg Zhurakousky - * @author Mark Sailes - * @author Rahul Lokurte - * @since 3.1.1 - * - */ -public final class CustomRuntimeEventLoop implements SmartLifecycle { - - private static Log logger = LogFactory.getLog(CustomRuntimeEventLoop.class); - - static final String LAMBDA_VERSION_DATE = "2018-06-01"; - private static final String LAMBDA_ERROR_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/{2}/error"; - private static final String LAMBDA_RUNTIME_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/next"; - private static final String LAMBDA_INVOCATION_URL_TEMPLATE = "http://{0}/{1}/runtime/invocation/{2}/response"; - private static final String USER_AGENT_VALUE = String.format( - "spring-cloud-function/%s-%s", - System.getProperty("java.runtime.version"), - extractVersion()); - - private final ConfigurableApplicationContext applicationContext; - - private volatile boolean running; - - private ExecutorService executor = Executors.newSingleThreadExecutor(); - - public CustomRuntimeEventLoop(ConfigurableApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - public void run() { - this.running = true; - this.executor.execute(() -> { - eventLoop(this.applicationContext); - }); - } - - @Override - public void start() { - this.run(); - } - - @Override - public void stop() { - this.executor.shutdownNow(); - this.running = false; - } - - @Override - public boolean isRunning() { - return this.running; - } - - private void eventLoop(ConfigurableApplicationContext context) { - Environment environment = context.getEnvironment(); - logger.info("Starting spring-cloud-function CustomRuntimeEventLoop"); - if (logger.isDebugEnabled()) { - logger.debug("AWS LAMBDA ENVIRONMENT: " + System.getenv()); - } - - String runtimeApi = environment.getProperty("AWS_LAMBDA_RUNTIME_API"); - String eventUri = MessageFormat.format(LAMBDA_RUNTIME_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE); - if (logger.isDebugEnabled()) { - logger.debug("Event URI: " + eventUri); - } - - RequestEntity requestEntity = RequestEntity.get(URI.create(eventUri)).header(USER_AGENT, USER_AGENT_VALUE).build(); - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - RestTemplate rest = new RestTemplate(); - JsonMapper mapper = context.getBean(JsonMapper.class); - - logger.info("Entering event loop"); - while (this.isRunning()) { - logger.debug("Attempting to get new event"); - ResponseEntity response = this.pollForData(rest, requestEntity); - - if (logger.isDebugEnabled()) { - logger.debug("New Event received: " + response); - } - - if (response != null && response.hasBody()) { - String requestId = response.getHeaders().getFirst("Lambda-Runtime-Aws-Request-Id"); - try { - FunctionInvocationWrapper function = locateFunction(environment, functionCatalog, response.getHeaders()); - - ByteArrayInputStream is = new ByteArrayInputStream(response.getBody().getBytes(StandardCharsets.UTF_8)); - Message requestMessage = AWSLambdaUtils.generateMessage(is, function.getInputType(), function.isSupplier(), mapper, null); - - Object functionResponse = function.apply(requestMessage); - byte[] responseBytes = AWSLambdaUtils.generateOutputFromObject(requestMessage, functionResponse, mapper, function.getOutputType()); - - String invocationUrl = MessageFormat - .format(LAMBDA_INVOCATION_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE, requestId); - - ResponseEntity result = rest.exchange(RequestEntity.post(URI.create(invocationUrl)) - .header(USER_AGENT, USER_AGENT_VALUE) - .body(responseBytes), Object.class); - - if (logger.isInfoEnabled()) { - logger.info("Result POST status: " + result); - } - } - catch (Exception e) { - this.propagateAwsError(requestId, e, mapper, runtimeApi, rest); - } - } - } - } - - private void propagateAwsError(String requestId, Exception e, JsonMapper mapper, String runtimeApi, RestTemplate rest) { - String errorMessage = e.getMessage(); - String errorType = e.getClass().getSimpleName(); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - String stackTrace = sw.toString(); - Map em = new HashMap<>(); - em.put("errorMessage", errorMessage); - em.put("errorType", errorType); - em.put("stackTrace", stackTrace); - byte[] outputBody = mapper.toJson(em); - try { - String errorUrl = MessageFormat.format(LAMBDA_ERROR_URL_TEMPLATE, runtimeApi, LAMBDA_VERSION_DATE, requestId); - ResponseEntity result = rest.exchange(RequestEntity.post(URI.create(errorUrl)) - .header(USER_AGENT, USER_AGENT_VALUE) - .body(outputBody), Object.class); - if (logger.isInfoEnabled()) { - logger.info("Result ERROR status: " + result.getStatusCode()); - } - } - catch (Exception e2) { - throw new IllegalArgumentException("Failed to report error", e2); - } - } - - private ResponseEntity pollForData(RestTemplate rest, RequestEntity requestEntity) { - try { - return rest.exchange(requestEntity, String.class); - } - catch (Exception e) { - if (e instanceof SocketException) { - this.stop(); - // ignore - } - } - return null; - } - - private FunctionInvocationWrapper locateFunction(Environment environment, FunctionCatalog functionCatalog, - HttpHeaders httpHeaders) { - MediaType contentType = httpHeaders.getContentType(); - String handlerName = environment.getProperty("DEFAULT_HANDLER"); - if (logger.isDebugEnabled()) { - logger.debug("Value of DEFAULT_HANDLER env: " + handlerName); - } - FunctionInvocationWrapper function = functionCatalog.lookup(handlerName, contentType.toString()); - if (function == null) { - logger.debug("Could not locate function under DEFAULT_HANDLER"); - handlerName = environment.getProperty("_HANDLER"); - if (logger.isDebugEnabled()) { - logger.debug("Value of _HANDLER env: " + handlerName); - } - function = functionCatalog.lookup(handlerName, contentType.toString()); - } - - if (function == null) { - logger.debug("Could not locate function under _HANDLER"); - function = functionCatalog.lookup((String) null, contentType.toString()); - } - - if (function == null) { - logger.info("Could not determine default function"); - handlerName = environment.getProperty("spring.cloud.function.definition"); - if (logger.isDebugEnabled()) { - logger.debug("Value of 'spring.cloud.function.definition' env: " + handlerName); - } - function = functionCatalog.lookup(handlerName, contentType.toString()); - } - - if (function == null) { - logger.info("Could not determine DEFAULT_HANDLER, _HANDLER or 'spring.cloud.function.definition'"); - handlerName = httpHeaders.getFirst("spring.cloud.function.definition"); - if (logger.isDebugEnabled()) { - logger.debug("Value of 'spring.cloud.function.definition' header: " + handlerName); - } - function = functionCatalog.lookup(handlerName, contentType.toString()); - } - - if (function == null) { - function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME, "application/json"); - if (function != null && logger.isInfoEnabled()) { - logger.info("Will default to RoutingFunction, since multiple functions available in FunctionCatalog." - + "Expecting 'spring.cloud.function.definition' or 'spring.cloud.function.routing-expression' as Message headers. " - + "If invocation is over API Gateway, Message headers can be provided as HTTP headers."); - } - } - - Assert.notNull(function, "Failed to locate function. Tried locating default function, " - + "function by 'DEFAULT_HANDLER', '_HANDLER' env variable as well as'spring.cloud.function.definition'. " - + "Functions available in catalog are: " + functionCatalog.getNames(null)); - if (function != null && logger.isInfoEnabled()) { - logger.info("Located function " + function.getFunctionDefinition()); - } - return function; - } - - private static String extractVersion() { - try { - String path = CustomRuntimeEventLoop.class.getProtectionDomain().getCodeSource().getLocation().toString(); - int endIndex = path.lastIndexOf('.'); - if (endIndex < 0) { - return "UNKNOWN-VERSION"; - } - int startIndex = path.lastIndexOf("/") + 1; - return path.substring(startIndex, endIndex).replace("spring-cloud-function-adapter-aws-", ""); - } - catch (Exception e) { - if (logger.isDebugEnabled()) { - logger.debug("Failed to detect version", e); - } - return "UNKNOWN-VERSION"; - } - - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java deleted file mode 100644 index 50294cb28..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2018-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.SmartLifecycle; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.env.Environment; -import org.springframework.util.StringUtils; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public class CustomRuntimeInitializer implements ApplicationContextInitializer { - - private static Log logger = LogFactory.getLog(CustomRuntimeInitializer.class); - - @Override - public void initialize(GenericApplicationContext context) { - Environment environment = context.getEnvironment(); - if (logger.isDebugEnabled()) { - logger.debug("AWS Environment: " + System.getenv()); - } - - if (!this.isWebExportEnabled(context) && isCustomRuntime(environment)) { - if (context.getBeanFactory().getBeanNamesForType(CustomRuntimeEventLoop.class, false, false).length == 0) { - context.registerBean(StringUtils.uncapitalize(CustomRuntimeEventLoop.class.getSimpleName()), - SmartLifecycle.class, () -> new CustomRuntimeEventLoop(context)); - } - } - } - - private boolean isCustomRuntime(Environment environment) { - String handler = environment.getProperty("_HANDLER"); - if (StringUtils.hasText(handler)) { - handler = handler.split(":")[0]; - logger.info("AWS Handler: " + handler); - try { - Class clazz = Thread.currentThread().getContextClassLoader().loadClass(handler); - if (FunctionInvoker.class.isAssignableFrom(clazz)) { - return false; - } - } - catch (Exception e) { - logger.debug("Will execute Lambda in Custom Runtime"); - return true; - } - } - return false; - } - - - private boolean isWebExportEnabled(GenericApplicationContext context) { - Boolean enabled = context.getEnvironment() - .getProperty("spring.cloud.function.web.export.enabled", Boolean.class); - return enabled != null && enabled; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvoker.java deleted file mode 100644 index 107ca418e..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvoker.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2019-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Set; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import com.fasterxml.jackson.databind.MapperFeature; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.SpringApplication; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.FunctionalSpringApplication; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.cloud.function.json.JacksonMapper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.cloud.function.utils.FunctionClassUtils; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.env.Environment; -import org.springframework.messaging.Message; -import org.springframework.util.Assert; -import org.springframework.util.StreamUtils; -import org.springframework.util.StringUtils; - -/** - * - * @author Oleg Zhurakousky - * @since 3.1 - * - * see - * https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format - */ -public class FunctionInvoker implements RequestStreamHandler { - - private static Log logger = LogFactory.getLog(FunctionInvoker.class); - - private JsonMapper jsonMapper; - - private FunctionInvocationWrapper function; - - private volatile String functionDefinition; - - private boolean started; - - public FunctionInvoker(String functionDefinition) { - this.functionDefinition = functionDefinition; - String lateInitialization = System.getenv("FUNCTION_INVOKER_LATE_INITIALIZATION"); - if (!StringUtils.hasText(lateInitialization) || !Boolean.parseBoolean(lateInitialization)) { - this.start(); - } - else { - logger.info("Spring Application Context will be initialized on first request"); - } - } - - public FunctionInvoker() { - this(null); - } - - @SuppressWarnings({ "rawtypes" }) - @Override - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - if (!this.started) { - this.start(); - } - Message requestMessage = AWSLambdaUtils - .generateMessage(input, this.function.getInputType(), this.function.isSupplier(), jsonMapper, context); - - Object response = this.function.apply(requestMessage); - byte[] responseBytes = AWSLambdaUtils.generateOutputFromObject(requestMessage, response, this.jsonMapper, function.getOutputType()); - StreamUtils.copy(responseBytes, output); - // any exception should propagate - } - - private void start() { - Class startClass = FunctionClassUtils.getStartClass(); - String[] properties = new String[] {"--spring.cloud.function.web.export.enabled=false", "--spring.main.web-application-type=none"}; - ConfigurableApplicationContext context = ApplicationContextInitializer.class.isAssignableFrom(startClass) - ? FunctionalSpringApplication.run(new Class[] {startClass, AWSCompanionAutoConfiguration.class}, properties) - : SpringApplication.run(new Class[] {startClass, AWSCompanionAutoConfiguration.class}, properties); - - Environment environment = context.getEnvironment(); - if (!StringUtils.hasText(this.functionDefinition)) { - this.functionDefinition = environment.getProperty(FunctionProperties.FUNCTION_DEFINITION); - } - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - this.jsonMapper = context.getBean(JsonMapper.class); - if (this.jsonMapper instanceof JacksonMapper) { - ((JacksonMapper) this.jsonMapper).configureObjectMapper(objectMapper -> { - if (!objectMapper.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)) { - objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); - } - }); - } - - if (logger.isInfoEnabled()) { - logger.info("Locating function: '" + this.functionDefinition + "'"); - } - - this.function = functionCatalog.lookup(this.functionDefinition, "application/json"); - - if (this.function == null) { - if (logger.isInfoEnabled()) { - if (!StringUtils.hasText(this.functionDefinition)) { - logger.info("Failed to determine default function. Please use 'spring.cloud.function.definition' property " - + "or pass function definition as a constructir argument to this FunctionInvoker"); - } - Set names = functionCatalog.getNames(null); - if (names.size() == 1) { - logger.info("Will default to RoutingFunction, since it is the only function available in FunctionCatalog." - + "Expecting 'spring.cloud.function.definition' or 'spring.cloud.function.routing-expression' as Message headers. " - + "If invocation is over API Gateway, Message headers can be provided as HTTP headers."); - } - else { - logger.info("More then one function is available in FunctionCatalog. " + names - + " Will default to RoutingFunction, " - + "Expecting 'spring.cloud.function.definition' or 'spring.cloud.function.routing-expression' as Message headers. " - + "If invocation is over API Gateway, Message headers can be provided as HTTP headers."); - } - } - this.function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME, "application/json"); - } - - if (this.function.isOutputTypePublisher()) { - this.function.setSkipOutputConversion(true); - } - Assert.notNull(this.function, "Failed to lookup function " + this.functionDefinition); - - this.functionDefinition = this.function.getFunctionDefinition(); - if (logger.isInfoEnabled()) { - logger.info("Located function: '" + this.functionDefinition + "'"); - } - this.started = true; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/LambdaDestinationResolver.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/LambdaDestinationResolver.java deleted file mode 100644 index 61b1f8bf9..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/LambdaDestinationResolver.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import java.util.function.Supplier; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.function.web.source.DestinationResolver; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; - -/** - * Implementation of {@link DestinationResolver}for AWS Lambda which resolves destination - * from `lambda-runtime-aws-request-id` message header. - * - * @author Dave Syer - * @author Oleg Zhurakousky - * - */ -public class LambdaDestinationResolver implements DestinationResolver { - - private static Log logger = LogFactory.getLog(LambdaDestinationResolver.class); - - @Override - public String destination(Supplier supplier, String name, Object value) { - if (logger.isDebugEnabled()) { - logger.debug("Lambda invoming value: " + value); - } - String destination = "unknown"; - if (value instanceof Message) { - Message message = (Message) value; - MessageHeaders headers = message.getHeaders(); - if (headers.containsKey("lambda-runtime-aws-request-id")) { - destination = (String) headers.get("lambda-runtime-aws-request-id"); - } - } - if (logger.isDebugEnabled()) { - logger.debug("Lambda destination resolved to: " + destination); - } - return destination; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/test/aws/AWSCustomRuntime.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/test/aws/AWSCustomRuntime.java deleted file mode 100644 index 4b396511a..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/test/aws/AWSCustomRuntime.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.test.aws; - -import java.util.Map; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.core.env.MapPropertySource; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeTypeUtils; - -/** - * AWS Custom Runtime emulator to be used for testing. - * - * @author Oleg Zhurakousky - * @since 3.2 - */ -@EnableAutoConfiguration -public class AWSCustomRuntime { - - BlockingQueue inputQueue = new ArrayBlockingQueue<>(3); - - BlockingQueue> outputQueue = new ArrayBlockingQueue<>(3); - - public AWSCustomRuntime(ConfigurableApplicationContext context) { - context.getEnvironment().getPropertySources().addFirst( - new MapPropertySource("AWSCustomRuntime", - Map.of("AWS_LAMBDA_RUNTIME_API", "localhost:${local.server.port}"))); - } - - @Bean("2018-06-01/runtime/invocation/consume/response") - Consumer> consume() { - return v -> outputQueue.offer(v); - } - - @SuppressWarnings("unchecked") - @Bean("2018-06-01/runtime/invocation/next") - Supplier> supply() { - - return () -> { - try { - Object value = inputQueue.poll(1L, TimeUnit.SECONDS); - if (value == null) { - return MessageBuilder.withPayload("").build(); - } - if (!(value instanceof Message)) { - return MessageBuilder.withPayload((String) value) - .setHeader("Lambda-Runtime-Aws-Request-Id", "consume") - .setHeader("Content-Type", - MimeTypeUtils.APPLICATION_JSON) - .build(); - } - else { - return (Message) value; - } - - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException(e); - } - }; - } - - public Message exchange(Object input) { - inputQueue.offer(input); - try { - return outputQueue.poll(5000, TimeUnit.MILLISECONDS); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return null; - } - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/resources/META-INF/spring.factories b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/resources/META-INF/spring.factories deleted file mode 100644 index f28be4c77..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.context.ApplicationContextInitializer=\ -org.springframework.cloud.function.adapter.aws.CustomRuntimeInitializer diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 939bd963c..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -org.springframework.cloud.function.adapter.aws.AWSCompanionAutoConfiguration diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoopTest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoopTest.java deleted file mode 100644 index 868c2ba01..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoopTest.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.adapter.test.aws.AWSCustomRuntime; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.stereotype.Component; -import org.springframework.test.annotation.DirtiesContext; - -import static org.assertj.core.api.Assertions.assertThat; -/** - * - * @author Oleg Zhurakousky - */ -public class CustomRuntimeEventLoopTest { - - private String API_EVENT = "{\n" - + " \"version\": \"1.0\",\n" - + " \"resource\": \"$default\",\n" - + " \"path\": \"/question\",\n" - + " \"httpMethod\": \"POST\",\n" - + " \"headers\": {\n" - + " \"Content-Length\": \"40\",\n" - + " \"Content-Type\": \"application/json\",\n" - + " \"Host\": \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\",\n" - + " \"User-Agent\": \"curl/7.88.1\",\n" - + " \"X-Amzn-Trace-Id\": \"Root=1-64ad9787-4c89d5af7607eb9e522e01d5\",\n" - + " \"X-Forwarded-For\": \"109.210.252.44\",\n" - + " \"X-Forwarded-Port\": \"443\",\n" - + " \"X-Forwarded-Proto\": \"https\",\n" - + " \"accept\": \"*/*\"\n" - + " },\n" - + " \"multiValueHeaders\": {\n" - + " \"Content-Length\": [\n" - + " \"40\"\n" - + " ],\n" - + " \"Content-Type\": [\n" - + " \"application/json\"\n" - + " ],\n" - + " \"Host\": [\n" - + " \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\"\n" - + " ],\n" - + " \"User-Agent\": [\n" - + " \"curl/7.88.1\"\n" - + " ],\n" - + " \"X-Amzn-Trace-Id\": [\n" - + " \"Root=1-64ad9787-4c89d5af7607eb9e522e01d5\"\n" - + " ],\n" - + " \"X-Forwarded-For\": [\n" - + " \"109.210.252.44\"\n" - + " ],\n" - + " \"X-Forwarded-Port\": [\n" - + " \"443\"\n" - + " ],\n" - + " \"X-Forwarded-Proto\": [\n" - + " \"https\"\n" - + " ],\n" - + " \"accept\": [\n" - + " \"*/*\"\n" - + " ]\n" - + " },\n" - + " \"queryStringParameters\": null,\n" - + " \"multiValueQueryStringParameters\": null,\n" - + " \"requestContext\": {\n" - + " \"accountId\": \"313369169943\",\n" - + " \"apiId\": \"emcdxu5ijj\",\n" - + " \"domainName\": \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\",\n" - + " \"domainPrefix\": \"emcdxu5ijj\",\n" - + " \"extendedRequestId\": \"H6SdPgXtiYcEP1w=\",\n" - + " \"httpMethod\": \"POST\",\n" - + " \"identity\": {\n" - + " \"accessKey\": null,\n" - + " \"accountId\": null,\n" - + " \"caller\": null,\n" - + " \"cognitoAmr\": null,\n" - + " \"cognitoAuthenticationProvider\": null,\n" - + " \"cognitoAuthenticationType\": null,\n" - + " \"cognitoIdentityId\": null,\n" - + " \"cognitoIdentityPoolId\": null,\n" - + " \"principalOrgId\": null,\n" - + " \"sourceIp\": \"109.210.252.44\",\n" - + " \"user\": null,\n" - + " \"userAgent\": \"curl/7.88.1\",\n" - + " \"userArn\": null\n" - + " },\n" - + " \"path\": \"/question\",\n" - + " \"protocol\": \"HTTP/1.1\",\n" - + " \"requestId\": \"H6SdPgXtiYcEP1w=\",\n" - + " \"requestTime\": \"11/Jul/2023:17:55:19 +0000\",\n" - + " \"requestTimeEpoch\": 1689098119662,\n" - + " \"resourceId\": \"$default\",\n" - + " \"resourcePath\": \"$default\",\n" - + " \"stage\": \"$default\"\n" - + " },\n" - + " \"pathParameters\": null,\n" - + " \"stageVariables\": null,\n" - + " \"body\": \"[{\\\"latitude\\\": 41.34, \\\"longitude\\\": 2.78},{\\\"latitude\\\": 43.24, \\\"longitude\\\": 3.78}]\",\n" - + " \"isBase64Encoded\": false\n" - + "}"; - - @Test - public void testDefaultFunctionLookup() throws Exception { - testDefaultFunctionLookup("uppercase", SingleFunctionConfiguration.class); - } - - @Test - public void testDefaultFunctionLookupReactive() throws Exception { - testDefaultFunctionLookup("uppercase", SingleFunctionConfigurationReactive.class); - } - - private void testDefaultFunctionLookup(String handler, Class context) throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(context, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=" + handler, "server.port=0") - .run()) { - - AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); - Message replyMessage = aws.exchange("\"ricky\""); - assertThat(replyMessage.getHeaders()).containsKey("user-agent"); - assertThat(((String) replyMessage.getHeaders().get("user-agent"))).startsWith("spring-cloud-function"); - assertThat(aws.exchange("\"ricky\"").getPayload()).isEqualTo("\"RICKY\""); - assertThat(aws.exchange("\"julien\"").getPayload()).isEqualTo("\"JULIEN\""); - assertThat(aws.exchange("\"bubbles\"").getPayload()).isEqualTo("\"BUBBLES\""); - } - } - - @Test - public void testDefaultFunctionAsComponentLookup() throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(PersonFunction.class, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=personFunction", "server.port=0") - .run()) { - - AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); - - assertThat(aws.exchange("\"ricky\"").getPayload()).isEqualTo("{\"name\":\"RICKY\"}"); - assertThat(aws.exchange("\"julien\"").getPayload()).isEqualTo("{\"name\":\"JULIEN\"}"); - assertThat(aws.exchange("\"bubbles\"").getPayload()).isEqualTo("{\"name\":\"BUBBLES\"}"); - } - } - - @Test - public void test_HANDLERlookupAndPojoFunction() throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(MultipleFunctionConfiguration.class, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=uppercasePerson", "server.port=0") - .run()) { - - AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); - - assertThat(aws.exchange("\"ricky\"").getPayload()).isEqualTo("{\"name\":\"RICKY\"}"); - assertThat(aws.exchange("\"julien\"").getPayload()).isEqualTo("{\"name\":\"JULIEN\"}"); - assertThat(aws.exchange("\"bubbles\"").getPayload()).isEqualTo("{\"name\":\"BUBBLES\"}"); - } - } - - @Test - public void test_HANDLERWithApiGatewayRequestAndFlux() throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(MultipleFunctionConfiguration.class, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=echoFlux", "server.port=0") - .run()) { - - AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); - String response = aws.exchange(API_EVENT).getPayload(); - assertThat(response).contains("{\\\"latitude\\\":2.78,\\\"longitude\\\":41.34}"); - assertThat(response).contains("{\\\"latitude\\\":3.78,\\\"longitude\\\":43.24}"); - } - } - - @Test - @DirtiesContext - public void test_definitionLookupAndComposition() throws Exception { - try (ConfigurableApplicationContext userContext = - new SpringApplicationBuilder(MultipleFunctionConfiguration.class, AWSCustomRuntime.class) - .web(WebApplicationType.SERVLET) - .properties("_HANDLER=toPersonJson|uppercasePerson", "server.port=0") - .run()) { - - AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class); - - assertThat(aws.exchange("\"ricky\"").getPayload()).isEqualTo("{\"name\":\"RICKY\"}"); - assertThat(aws.exchange("\"julien\"").getPayload()).isEqualTo("{\"name\":\"JULIEN\"}"); - assertThat(aws.exchange("\"bubbles\"").getPayload()).isEqualTo("{\"name\":\"BUBBLES\"}"); - } - - } - - @EnableAutoConfiguration - protected static class SingleFunctionConfiguration { - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - } - - @EnableAutoConfiguration - protected static class SingleFunctionConfigurationReactive { - @Bean - public Function, Flux> uppercase() { - return v -> v.map(String::toUpperCase); - } - } - - @EnableAutoConfiguration - @Configuration(proxyBeanMethods = false) - protected static class MultipleFunctionConfiguration { - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - @Bean - public Function toPersonJson() { - return v -> "{\"name\":\"" + v + "\"}"; - } - - @Bean - public Function uppercasePerson() { - return p -> new Person(p.getName().toUpperCase()); - } - - @Bean - public Function, Flux> echoFlux() { - return flux -> flux.map(g -> { - return new GeoLocation(g.longitude(), g.latitude()); - }); - } - } - - @EnableAutoConfiguration - @Component("personFunction") // need in test explicitly since it is inner class and name wil be `customRuntimeEventLoopTest.PersonFunction` - public static class PersonFunction implements Function { - - public PersonFunction() { - System.out.println(); - } - - @Override - public Person apply(Person input) { - return new Person(input.getName().toUpperCase()); - } - } - - public static class Person { - private String name; - - public Person() { - - } - - public Person(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } - - - public record GeoLocation(Float latitude, Float longitude) { - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/FunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/FunctionInvokerTests.java deleted file mode 100644 index 83959248e..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/FunctionInvokerTests.java +++ /dev/null @@ -1,1830 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.Collections; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.APIGatewayCustomAuthorizerEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse; -import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; -import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerResponseEvent; -import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; -import com.amazonaws.services.lambda.runtime.events.KinesisEvent; -import com.amazonaws.services.lambda.runtime.events.S3Event; -import com.amazonaws.services.lambda.runtime.events.SNSEvent; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.cloud.function.json.JacksonMapper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.messaging.converter.AbstractMessageConverter; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeType; -import org.springframework.util.StreamUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class FunctionInvokerTests { - - ObjectMapper mapper = new ObjectMapper(); - - String jsonCollection = "[\"Ricky\",\"Julien\",\"Bubbles\"]"; - - String jsonPojoCollection = "[{\"name\":\"Ricky\"},{\"name\":\"Julien\"},{\"name\":\"Julien\"}]"; - - String dynamoDbEvent = "{\n" - + " \"Records\": [\n" - + " {\n" - + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e69f274ee\",\n" - + " \"eventName\": \"INSERT\",\n" - + " \"eventVersion\": \"1.1\",\n" - + " \"eventSource\": \"aws:dynamodb\",\n" - + " \"awsRegion\": \"us-east-1\",\n" - + " \"userIdentity\":{\n" - + " \"type\":\"Service\",\n" - + " \"principalId\":\"dynamodb.amazonaws.com\"\n" - + " },\n" - + " \"dynamodb\": {\n" - + " \"ApproximateCreationDateTime\": 1.684934517E9,\n" - + " \"Keys\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " }\n" - + " },\n" - + " \"NewImage\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"asdf1\": {\n" - + " \"B\": \"AAEqQQ==\"\n" - + " },\n" - + " \"asdf2\": {\n" - + " \"BS\": [\n" - + " \"AAEqQQ==\",\n" - + " \"QSoBAA==\"\n" - + " ]\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " }\n" - + " },\n" - + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" - + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" - + " },\n" - + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " },\n" - + " {\n" - + " \"eventID\": \"f07f8ca4b0b26cb9c4e5e77e42f274ee\",\n" - + " \"eventName\": \"INSERT\",\n" - + " \"eventVersion\": \"1.1\",\n" - + " \"eventSource\": \"aws:dynamodb\",\n" - + " \"awsRegion\": \"us-east-1\",\n" - + " \"dynamodb\": {\n" - + " \"ApproximateCreationDateTime\": 1480642020,\n" - + " \"Keys\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " }\n" - + " },\n" - + " \"NewImage\": {\n" - + " \"val\": {\n" - + " \"S\": \"data\"\n" - + " },\n" - + " \"asdf1\": {\n" - + " \"B\": \"AAEqQQ==\"\n" - + " },\n" - + " \"b2\": {\n" - + " \"B\": \"test\"\n" - + " },\n" - + " \"asdf2\": {\n" - + " \"BS\": [\n" - + " \"AAEqQQ==\",\n" - + " \"QSoBAA==\",\n" - + " \"AAEqQQ==\"\n" - + " ]\n" - + " },\n" - + " \"key\": {\n" - + " \"S\": \"binary\"\n" - + " },\n" - + " \"Binary\": {\n" - + " \"B\": \"AAEqQQ==\"\n" - + " },\n" - + " \"Boolean\": {\n" - + " \"BOOL\": true\n" - + " },\n" - + " \"BinarySet\": {\n" - + " \"BS\": [\n" - + " \"AAEqQQ==\",\n" - + " \"AAEqQQ==\"\n" - + " ]\n" - + " },\n" - + " \"List\": {\n" - + " \"L\": [\n" - + " {\n" - + " \"S\": \"Cookies\"\n" - + " },\n" - + " {\n" - + " \"S\": \"Coffee\"\n" - + " },\n" - + " {\n" - + " \"N\": \"3.14159\"\n" - + " }\n" - + " ]\n" - + " },\n" - + " \"Map\": {\n" - + " \"M\": {\n" - + " \"Name\": {\n" - + " \"S\": \"Joe\"\n" - + " },\n" - + " \"Age\": {\n" - + " \"N\": \"35\"\n" - + " }\n" - + " }\n" - + " },\n" - + " \"FloatNumber\": {\n" - + " \"N\": \"123.45\"\n" - + " },\n" - + " \"IntegerNumber\": {\n" - + " \"N\": \"123\"\n" - + " },\n" - + " \"NumberSet\": {\n" - + " \"NS\": [\n" - + " \"1234\",\n" - + " \"567.8\"\n" - + " ]\n" - + " },\n" - + " \"Null\": {\n" - + " \"NULL\": true\n" - + " },\n" - + " \"String\": {\n" - + " \"S\": \"Hello\"\n" - + " },\n" - + " \"StringSet\": {\n" - + " \"SS\": [\n" - + " \"Giraffe\",\n" - + " \"Zebra\"\n" - + " ]\n" - + " },\n" - + " \"EmptyStringSet\": {\n" - + " \"SS\": []\n" - + " }\n" - + " },\n" - + " \"SequenceNumber\": \"1405400000000002063282832\",\n" - + " \"SizeBytes\": 54,\n" - + " \"StreamViewType\": \"NEW_AND_OLD_IMAGES\"\n" - + " },\n" - + " \"eventSourceARN\": \"arn:aws:dynamodb:us-east-1:123456789012:table/Example-Table/stream/2016-12-01T00:00:00.000\"\n" - + " }\n" - + " ]\n" - + "}"; - - String sampleLBEvent = "{\n" - + " \"requestContext\": {\n" - + " \"elb\": {\n" - + " \"targetGroupArn\": \"arn:aws:elasticloadbalancing:us-east-1:XXXXXXXXXXX:targetgroup/sample/6d0ecf831eec9f09\"\n" - + " }\n" - + " },\n" - + " \"httpMethod\": \"GET\",\n" - + " \"path\": \"/\",\n" - + " \"queryStringParameters\": {},\n" - + " \"headers\": {\n" - + " \"accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n" - + " \"accept-encoding\": \"gzip\",\n" - + " \"accept-language\": \"en-US,en;q=0.5\",\n" - + " \"connection\": \"keep-alive\",\n" - + " \"cookie\": \"name=value\",\n" - + " \"host\": \"lambda-YYYYYYYY.elb.amazonaws.com\",\n" - + " \"upgrade-insecure-requests\": \"1\",\n" - + " \"user-agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:60.0) Gecko/20100101 Firefox/60.0\",\n" - + " \"x-amzn-trace-id\": \"Root=1-5bdb40ca-556d8b0c50dc66f0511bf520\",\n" - + " \"x-forwarded-for\": \"192.0.2.1\",\n" - + " \"x-forwarded-port\": \"80\",\n" - + " \"x-forwarded-proto\": \"http\"\n" - + " },\n" - + " \"body\": \"Hello from ELB\",\n" - + " \"isBase64Encoded\": false\n" - + "}"; - - String sampleSQSEvent = "{\n" + - " \"Records\": [\n" + - " {\n" + - " \"messageId\": \"19dd0b57-b21e-4ac1-bd88-01bbb068cb78\",\n" + - " \"receiptHandle\": \"MessageReceiptHandle\",\n" + - " \"body\": \"Hello from SQS!\",\n" + - " \"attributes\": {\n" + - " \"ApproximateReceiveCount\": \"1\",\n" + - " \"SentTimestamp\": \"1523232000000\",\n" + - " \"SenderId\": \"123456789012\",\n" + - " \"ApproximateFirstReceiveTimestamp\": \"1523232000001\"\n" + - " },\n" + - " \"messageAttributes\": {},\n" + - " \"md5OfBody\": \"7b270e59b47ff90a553787216d55d91d\",\n" + - " \"eventSource\": \"aws:sqs\",\n" + - " \"eventSourceARN\": \"arn:aws:sqs:eu-central-1:123456789012:MyQueue\",\n" + - " \"awsRegion\": \"eu-central-1\"\n" + - " }\n" + - " ]\n" + - "}"; - - String sampleSNSEvent = "{\n" + - " \"Records\": [\n" + - " {\n" + - " \"EventVersion\": \"1.0\",\n" + - " \"EventSubscriptionArn\": \"arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + - " \"EventSource\": \"aws:sns\",\n" + - " \"Sns\": {\n" + - " \"SignatureVersion\": \"1\",\n" + - " \"Timestamp\": \"2019-01-02T12:45:07.000Z\",\n" + - " \"Signature\": \"tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==\",\n" + - " \"SigningCertUrl\": \"https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem\",\n" + - " \"MessageId\": \"95df01b4-ee98-5cb9-9903-4c221d41eb5e\",\n" + - " \"Message\": \"Hello from SNS!\",\n" + - " \"MessageAttributes\": {\n" + - " \"Test\": {\n" + - " \"Type\": \"String\",\n" + - " \"Value\": \"TestString\"\n" + - " },\n" + - " \"TestBinary\": {\n" + - " \"Type\": \"Binary\",\n" + - " \"Value\": \"TestBinary\"\n" + - " }\n" + - " },\n" + - " \"Type\": \"Notification\",\n" + - " \"UnsubscribeUrl\": \"https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + - " \"TopicArn\":\"arn:aws:sns:us-east-2:123456789012:sns-lambda\",\n" + - " \"Subject\": \"TestInvoke\"\n" + - " }\n" + - " }\n" + - " ]\n" + - "}"; - - String sampleKinesisEvent = "{" + - " \"Records\": [" + - " {" + - " \"kinesis\": {" + - " \"kinesisSchemaVersion\": \"1.0\"," + - " \"partitionKey\": \"1\"," + - " \"sequenceNumber\": \"49590338271490256608559692538361571095921575989136588898\"," + - " \"data\": \"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==\"," + - " \"approximateArrivalTimestamp\": 1545084650.987" + - " }," + - " \"eventSource\": \"aws:kinesis\"," + - " \"eventVersion\": \"1.0\"," + - " \"eventID\": \"shardId-000000000006:49590338271490256608559692538361571095921575989136588898\"," + - " \"eventName\": \"aws:kinesis:record\"," + - " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\"," + - " \"awsRegion\": \"us-east-2\"," + - " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"" + - " }," + - " {" + - " \"kinesis\": {" + - " \"kinesisSchemaVersion\": \"1.0\"," + - " \"partitionKey\": \"1\"," + - " \"sequenceNumber\": \"49590338271490256608559692540925702759324208523137515618\"," + - " \"data\": \"VGhpcyBpcyBvbmx5IGEgdGVzdC4=\"," + - " \"approximateArrivalTimestamp\": 1545084711.166" + - " }," + - " \"eventSource\": \"aws:kinesis\"," + - " \"eventVersion\": \"1.0\"," + - " \"eventID\": \"shardId-000000000006:49590338271490256608559692540925702759324208523137515618\"," + - " \"eventName\": \"aws:kinesis:record\"," + - " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\"," + - " \"awsRegion\": \"us-east-2\"," + - " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"" + - " }" + - " ]" + - "}"; - - //https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html - String apiGatewayV2Event = "{\n" + - " \"version\": \"2.0\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"rawPath\": \"/my/path\",\n" + - " \"rawQueryString\": \"parameter1=value1¶meter1=value2¶meter2=value\",\n" + - " \"cookies\": [\n" + - " \"cookie1\",\n" + - " \"cookie2\"\n" + - " ],\n" + - " \"headers\": {\n" + - " \"header1\": \"value1\",\n" + - " \"header2\": \"value1,value2\"\n" + - " },\n" + - " \"queryStringParameters\": {\n" + - " \"parameter1\": \"value1,value2\",\n" + - " \"parameter2\": \"value\"\n" + - " },\n" + - " \"requestContext\": {\n" + - " \"accountId\": \"123456789012\",\n" + - " \"apiId\": \"api-id\",\n" + - " \"authentication\": {\n" + - " \"clientCert\": {\n" + - " \"clientCertPem\": \"CERT_CONTENT\",\n" + - " \"subjectDN\": \"www.example.com\",\n" + - " \"issuerDN\": \"Example issuer\",\n" + - " \"serialNumber\": \"a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1\",\n" + - " \"validity\": {\n" + - " \"notBefore\": \"May 28 12:30:02 2019 GMT\",\n" + - " \"notAfter\": \"Aug 5 09:36:04 2021 GMT\"\n" + - " }\n" + - " }\n" + - " },\n" + - " \"authorizer\": {\n" + - " \"jwt\": {\n" + - " \"claims\": {\n" + - " \"claim1\": \"value1\",\n" + - " \"claim2\": \"value2\"\n" + - " },\n" + - " \"scopes\": [\n" + - " \"scope1\",\n" + - " \"scope2\"\n" + - " ]\n" + - " }\n" + - " },\n" + - " \"domainName\": \"id.execute-api.us-east-1.amazonaws.com\",\n" + - " \"domainPrefix\": \"id\",\n" + - " \"http\": {\n" + - " \"method\": \"POST\",\n" + - " \"path\": \"/my/path\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"sourceIp\": \"IP\",\n" + - " \"userAgent\": \"agent\"\n" + - " },\n" + - " \"requestId\": \"id\",\n" + - " \"routeKey\": \"$default\",\n" + - " \"stage\": \"$default\",\n" + - " \"time\": \"12/Mar/2020:19:03:58 +0000\",\n" + - " \"timeEpoch\": 1583348638390\n" + - " },\n" + - " \"body\": \"Hello from Lambda\",\n" + - " \"pathParameters\": {\n" + - " \"parameter1\": \"value1\"\n" + - " },\n" + - " \"isBase64Encoded\": false,\n" + - " \"stageVariables\": {\n" + - " \"stageVariable1\": \"value1\",\n" + - " \"stageVariable2\": \"value2\"\n" + - " }\n" + - "}"; - - String apiGatewayEvent = "{\n" + - " \"resource\": \"/uppercase2\",\n" + - " \"path\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"headers\": {\n" + - " \"accept\": \"*/*\",\n" + - " \"content-type\": \"application/json\",\n" + - " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"User-Agent\": \"curl/7.54.0\",\n" + - " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" + - " \"X-Forwarded-For\": \"90.37.8.133\",\n" + - " \"X-Forwarded-Port\": \"443\",\n" + - " \"X-Forwarded-Proto\": \"https\"\n" + - " },\n" + - " \"multiValueHeaders\": {\n" + - " \"accept\": [\n" + - " \"*/*\"\n" + - " ],\n" + - " \"content-type\": [\n" + - " \"application/json\"\n" + - " ],\n" + - " \"Host\": [\n" + - " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + - " ],\n" + - " \"User-Agent\": [\n" + - " \"curl/7.54.0\"\n" + - " ],\n" + - " \"X-Amzn-Trace-Id\": [\n" + - " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" + - " ],\n" + - " \"X-Forwarded-For\": [\n" + - " \"90.37.8.133\"\n" + - " ],\n" + - " \"X-Forwarded-Port\": [\n" + - " \"443\"\n" + - " ],\n" + - " \"X-Forwarded-Proto\": [\n" + - " \"https\"\n" + - " ]\n" + - " },\n" + - " \"queryStringParameters\": null,\n" + - " \"multiValueQueryStringParameters\": null,\n" + - " \"pathParameters\": null,\n" + - " \"stageVariables\": null,\n" + - " \"requestContext\": {\n" + - " \"resourceId\": \"qf0io6\",\n" + - " \"resourcePath\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" + - " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + - " \"path\": \"/test/uppercase2\",\n" + - " \"accountId\": \"123456789098\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"stage\": \"test\",\n" + - " \"domainPrefix\": \"fhul32ccy2\",\n" + - " \"requestTimeEpoch\": 1590571934872,\n" + - " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + - " \"identity\": {\n" + - " \"cognitoIdentityPoolId\": null,\n" + - " \"accountId\": null,\n" + - " \"cognitoIdentityId\": null,\n" + - " \"caller\": null,\n" + - " \"sourceIp\": \"90.37.8.133\",\n" + - " \"principalOrgId\": null,\n" + - " \"accessKey\": null,\n" + - " \"cognitoAuthenticationType\": null,\n" + - " \"cognitoAuthenticationProvider\": null,\n" + - " \"userArn\": null,\n" + - " \"userAgent\": \"curl/7.54.0\",\n" + - " \"user\": null\n" + - " },\n" + - " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"apiId\": \"fhul32ccy2\"\n" + - " },\n" + - " \"body\":\"hello\",\n" + - " \"isBase64Encoded\": false\n" + - "}"; - - String s3Event = "{\n" + - " \"Records\":[\n" + - " {\n" + - " \"eventVersion\":\"2.1\",\n" + - " \"eventSource\":\"aws:s3\",\n" + - " \"awsRegion\":\"us-east-2\",\n" + - " \"eventTime\":\"2020-07-15T21:29:41.365Z\",\n" + - " \"eventName\":\"ObjectCreated:Put\",\n" + - " \"userIdentity\":{\n" + - " \"principalId\":\"AWS:AIxxx\"\n" + - " },\n" + - " \"requestParameters\":{\n" + - " \"sourceIPAddress\":\"xxxx\"\n" + - " },\n" + - " \"responseElements\":{\n" + - " \"x-amz-request-id\":\"xxxx\",\n" + - " \"x-amz-id-2\":\"xxx/=\"\n" + - " },\n" + - " \"s3\":{\n" + - " \"s3SchemaVersion\":\"1.0\",\n" + - " \"configurationId\":\"New Data Delivery\",\n" + - " \"bucket\":{\n" + - " \"name\":\"bucket\",\n" + - " \"ownerIdentity\":{\n" + - " \"principalId\":\"xxx\"\n" + - " },\n" + - " \"arn\":\"arn:aws:s3:::bucket\"\n" + - " },\n" + - " \"object\":{\n" + - " \"key\":\"test/file.geojson\",\n" + - " \"size\":32711,\n" + - " \"eTag\":\"aaaa\",\n" + - " \"sequencer\":\"aaaa\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - "}"; - - String apiGatewayEventWithStructuredBody = "{\n" + - " \"resource\": \"/uppercase2\",\n" + - " \"path\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"headers\": {\n" + - " \"accept\": \"*/*\",\n" + - " \"content-type\": \"application/json\",\n" + - " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"User-Agent\": \"curl/7.54.0\",\n" + - " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" + - " \"X-Forwarded-For\": \"90.37.8.133\",\n" + - " \"X-Forwarded-Port\": \"443\",\n" + - " \"X-Forwarded-Proto\": \"https\"\n" + - " },\n" + - " \"multiValueHeaders\": {\n" + - " \"accept\": [\n" + - " \"*/*\"\n" + - " ],\n" + - " \"content-type\": [\n" + - " \"application/json\"\n" + - " ],\n" + - " \"Host\": [\n" + - " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + - " ],\n" + - " \"User-Agent\": [\n" + - " \"curl/7.54.0\"\n" + - " ],\n" + - " \"X-Amzn-Trace-Id\": [\n" + - " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" + - " ],\n" + - " \"X-Forwarded-For\": [\n" + - " \"90.37.8.133\"\n" + - " ],\n" + - " \"X-Forwarded-Port\": [\n" + - " \"443\"\n" + - " ],\n" + - " \"X-Forwarded-Proto\": [\n" + - " \"https\"\n" + - " ]\n" + - " },\n" + - " \"queryStringParameters\": null,\n" + - " \"multiValueQueryStringParameters\": null,\n" + - " \"pathParameters\": null,\n" + - " \"stageVariables\": null,\n" + - " \"requestContext\": {\n" + - " \"resourceId\": \"qf0io6\",\n" + - " \"resourcePath\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" + - " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + - " \"path\": \"/test/uppercase2\",\n" + - " \"accountId\": \"123456789098\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"stage\": \"test\",\n" + - " \"domainPrefix\": \"fhul32ccy2\",\n" + - " \"requestTimeEpoch\": 1590571934872,\n" + - " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + - " \"identity\": {\n" + - " \"cognitoIdentityPoolId\": null,\n" + - " \"accountId\": null,\n" + - " \"cognitoIdentityId\": null,\n" + - " \"caller\": null,\n" + - " \"sourceIp\": \"90.37.8.133\",\n" + - " \"principalOrgId\": null,\n" + - " \"accessKey\": null,\n" + - " \"cognitoAuthenticationType\": null,\n" + - " \"cognitoAuthenticationProvider\": null,\n" + - " \"userArn\": null,\n" + - " \"userAgent\": \"curl/7.54.0\",\n" + - " \"user\": null\n" + - " },\n" + - " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"apiId\": \"fhul32ccy2\"\n" + - " },\n" + - " \"body\":{\"name\":\"Jim Lahey\"},\n" + - " \"isBase64Encoded\": false\n" + - "}"; - - String apiGatewayEventWithArray = "{\n" + - " \"resource\": \"/uppercase2\",\n" + - " \"path\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"headers\": {\n" + - " \"accept\": \"*/*\",\n" + - " \"content-type\": \"application/json\",\n" + - " \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"User-Agent\": \"curl/7.54.0\",\n" + - " \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" + - " \"X-Forwarded-For\": \"90.37.8.133\",\n" + - " \"X-Forwarded-Port\": \"443\",\n" + - " \"X-Forwarded-Proto\": \"https\"\n" + - " },\n" + - " \"multiValueHeaders\": {\n" + - " \"accept\": [\n" + - " \"*/*\"\n" + - " ],\n" + - " \"content-type\": [\n" + - " \"application/json\"\n" + - " ],\n" + - " \"Host\": [\n" + - " \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" + - " ],\n" + - " \"User-Agent\": [\n" + - " \"curl/7.54.0\"\n" + - " ],\n" + - " \"X-Amzn-Trace-Id\": [\n" + - " \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" + - " ],\n" + - " \"X-Forwarded-For\": [\n" + - " \"90.37.8.133\"\n" + - " ],\n" + - " \"X-Forwarded-Port\": [\n" + - " \"443\"\n" + - " ],\n" + - " \"X-Forwarded-Proto\": [\n" + - " \"https\"\n" + - " ]\n" + - " },\n" + - " \"queryStringParameters\": null,\n" + - " \"multiValueQueryStringParameters\": null,\n" + - " \"pathParameters\": null,\n" + - " \"stageVariables\": null,\n" + - " \"requestContext\": {\n" + - " \"resourceId\": \"qf0io6\",\n" + - " \"resourcePath\": \"/uppercase2\",\n" + - " \"httpMethod\": \"POST\",\n" + - " \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" + - " \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" + - " \"path\": \"/test/uppercase2\",\n" + - " \"accountId\": \"123456789098\",\n" + - " \"protocol\": \"HTTP/1.1\",\n" + - " \"stage\": \"test\",\n" + - " \"domainPrefix\": \"fhul32ccy2\",\n" + - " \"requestTimeEpoch\": 1590571934872,\n" + - " \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" + - " \"identity\": {\n" + - " \"cognitoIdentityPoolId\": null,\n" + - " \"accountId\": null,\n" + - " \"cognitoIdentityId\": null,\n" + - " \"caller\": null,\n" + - " \"sourceIp\": \"90.37.8.133\",\n" + - " \"principalOrgId\": null,\n" + - " \"accessKey\": null,\n" + - " \"cognitoAuthenticationType\": null,\n" + - " \"cognitoAuthenticationProvider\": null,\n" + - " \"userArn\": null,\n" + - " \"userAgent\": \"curl/7.54.0\",\n" + - " \"user\": null\n" + - " },\n" + - " \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" + - " \"apiId\": \"fhul32ccy2\"\n" + - " },\n" + - " \"body\":[{\"name\":\"Jim Lahey\"},{\"name\":\"Ricky\"}],\n" + - " \"isBase64Encoded\": false\n" + - "}"; - - String gwAuthorizerEvent = "{\n" - + " \"type\":\"TOKEN\",\n" - + " \"authorizationToken\":\"allow\",\n" - + " \"methodArn\":\"arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/\"\n" - + "}"; - - @BeforeEach - public void before() throws Exception { - System.clearProperty("MAIN_CLASS"); - System.clearProperty("spring.cloud.function.routing-expression"); - System.clearProperty("spring.cloud.function.definition"); - //this.getEnvironment().clear(); - } - - @Test - public void testAPIGatewayCustomAuthorizerEvent() throws Exception { - System.setProperty("MAIN_CLASS", AuthorizerConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "acceptAuthorizerEvent"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.gwAuthorizerEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("APIGatewayCustomAuthorizerEvent(version=null, type=TOKEN"); - } - - @Test - public void testCollection() throws Exception { - System.setProperty("MAIN_CLASS", SampleConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoStringReactive"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.jsonCollection.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).isEqualTo(this.jsonCollection); - } - - @Test - public void testCollectionPojo() throws Exception { - System.setProperty("MAIN_CLASS", SampleConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoPojoReactive"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.jsonPojoCollection.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).isEqualTo(this.jsonPojoCollection); - } - - @Test - public void testKinesisStringEvent() throws Exception { - System.setProperty("MAIN_CLASS", KinesisConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoString"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleKinesisEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("kinesisSchemaVersion"); - } - - @Test - public void testKinesisEvent() throws Exception { - System.setProperty("MAIN_CLASS", KinesisConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputKinesisEvent"); - FunctionInvoker invoker = new FunctionInvoker() { - @Override - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - assertThat(context).isNotNull(); - super.handleRequest(input, output, context); - } - }; - - InputStream targetStream = new ByteArrayInputStream(this.sampleKinesisEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, new TestContext()); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("49590338271490256608559692538361571095921575989136588898"); - } - - @Test - public void testKinesisEventAsMessage() throws Exception { - System.setProperty("MAIN_CLASS", KinesisConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputKinesisEventAsMessage"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleKinesisEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("49590338271490256608559692538361571095921575989136588898"); - } - - @Test - public void testKinesisEventAsMap() throws Exception { - System.setProperty("MAIN_CLASS", KinesisConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputKinesisEventAsMap"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleKinesisEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("49590338271490256608559692538361571095921575989136588898"); - } - - @Test - public void testSQSStringEvent() throws Exception { - System.setProperty("MAIN_CLASS", SQSConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoString"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleSQSEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result.length()).isEqualTo(14); // some additional JSON formatting - } - - @Test - public void testDynamoDb() throws Exception { - System.setProperty("MAIN_CLASS", DynamoDbConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "consume"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.dynamoDbEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - } - - @Test - public void testSQSEvent() throws Exception { - System.setProperty("MAIN_CLASS", SQSConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputSQSEvent"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleSQSEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("arn:aws:sqs:eu-central-1:123456789012:MyQueue"); - } - - @Test - public void testSQSEventWithConstructorArg() throws Exception { - System.setProperty("MAIN_CLASS", SQSConfiguration.class.getName()); - FunctionInvoker invoker = new FunctionInvoker("inputSQSEvent"); - - InputStream targetStream = new ByteArrayInputStream(this.sampleSQSEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("arn:aws:sqs:eu-central-1:123456789012:MyQueue"); - } - - @Test - public void testSQSEventAsMessage() throws Exception { - System.setProperty("MAIN_CLASS", SQSConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputSQSEventAsMessage"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleSQSEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("arn:aws:sqs:eu-central-1:123456789012:MyQueue"); - } - - @Test - public void testSQSEventAsMap() throws Exception { - System.setProperty("MAIN_CLASS", SQSConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputSQSEventAsMap"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleSQSEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("arn:aws:sqs:eu-central-1:123456789012:MyQueue"); - } - - @Test - public void testSNSStringEvent() throws Exception { - System.setProperty("MAIN_CLASS", SNSConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoString"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleSNSEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("arn:aws:sns"); - } - - @Test - public void testSNSEvent() throws Exception { - System.setProperty("MAIN_CLASS", SNSConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputSNSEvent"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleSNSEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("arn:aws:sns"); - } - - @Test - public void testSNSEventAsMessage() throws Exception { - System.setProperty("MAIN_CLASS", SNSConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputSNSEventAsMessage"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleSNSEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("arn:aws:sns"); - } - - @Test - public void testSNSEventAsMap() throws Exception { - System.setProperty("MAIN_CLASS", SNSConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputSNSEventAsMap"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleSNSEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("arn:aws:sns"); - } - - @Test - public void testS3StringEvent() throws Exception { - System.setProperty("MAIN_CLASS", S3Configuration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoString"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.s3Event.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("s3SchemaVersion"); - } - - @Test - public void testS3Event() throws Exception { - System.setProperty("MAIN_CLASS", S3Configuration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputS3Event"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.s3Event.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("ObjectCreated:Put"); - } - - @Test - public void testS3EventAsMessage() throws Exception { - System.setProperty("MAIN_CLASS", S3Configuration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputS3EventAsMessage"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.s3Event.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("ObjectCreated:Put"); - } - - @Test - public void testS3EventAsMap() throws Exception { - System.setProperty("MAIN_CLASS", S3Configuration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputS3EventAsMap"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.s3Event.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("s3SchemaVersion"); - } - - - @Test - public void testLBEventStringInOut() throws Exception { - System.setProperty("MAIN_CLASS", LBConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoString"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleLBEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"Hello from ELB\""); - } - - @Test - public void testS3EventReactive() throws Exception { - System.setProperty("MAIN_CLASS", S3Configuration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoStringFlux"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.s3Event.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - assertThat(result).contains("s3SchemaVersion"); - } - - @Test - public void testLBEvent() throws Exception { - System.setProperty("MAIN_CLASS", LBConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputLBEvent"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleLBEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"Hello from ELB\""); - } - - @Test - public void testLBEventAsMessage() throws Exception { - System.setProperty("MAIN_CLASS", LBConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputLBEventAsMessage"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.sampleLBEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, Mockito.mock(Context.class)); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"Hello from ELB\""); - } - - @Test - public void testLBEventInOut() throws Exception { - System.setProperty("MAIN_CLASS", LBConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputOutputLBEvent"); - FunctionInvoker invoker = new FunctionInvoker() { - @Override - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - assertThat(context).isNotNull(); - super.handleRequest(input, output, context); - } - }; - - InputStream targetStream = new ByteArrayInputStream(this.sampleLBEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, new TestContext()); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("Hello from ELB"); - } - - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayStringEventBody() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "uppercase"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"HELLO\""); - - System.clearProperty("spring.cloud.function.definition"); - System.setProperty("spring.cloud.function.routing-expression", "'uppercase'"); - invoker = new FunctionInvoker(); - targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - result = this.mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"HELLO\""); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayPojoReturninPojo() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "uppercasePojoReturnPojo"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEventWithStructuredBody.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map response = mapper.readValue(output.toByteArray(), Map.class); - Person person = mapper.readValue((String) response.get("body"), Person.class); - assertThat(person.getName()).isEqualTo("JIM LAHEY"); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayPojoReturninPojoReactive() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "uppercasePojoReturnPojoReactive"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEventWithArray.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map response = mapper.readValue(output.toByteArray(), Map.class); - assertThat(response.get("body").toString()).isEqualTo("[{\"name\":\"JIM LAHEY\"},{\"name\":\"RICKY\"}]"); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayPojoEventBody() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "uppercasePojo"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEventWithStructuredBody.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"JIM LAHEY\""); - - System.clearProperty("spring.cloud.function.definition"); - System.setProperty("spring.cloud.function.routing-expression", "'uppercasePojo'"); - invoker = new FunctionInvoker(); - targetStream = new ByteArrayInputStream(this.apiGatewayEventWithStructuredBody.getBytes()); - output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - result = this.mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"JIM LAHEY\""); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayEvent() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputApiEvent"); - FunctionInvoker invoker = new FunctionInvoker() { - @Override - public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { - assertThat(context).isNotNull(); - super.handleRequest(input, output, context); - } - }; - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, new TestContext()); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - System.out.println(result); - assertThat(result.get("body")).isEqualTo("\"hello\""); - - System.clearProperty("spring.cloud.function.definition"); - System.setProperty("spring.cloud.function.routing-expression", "'inputApiEvent'"); - invoker = new FunctionInvoker(); - targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - result = this.mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"hello\""); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayV2Event() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputApiV2Event"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayV2Event.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - System.out.println(result); - assertThat(result.get("body")).isEqualTo("\"Hello from Lambda\""); - - System.clearProperty("spring.cloud.function.definition"); - System.setProperty("spring.cloud.function.routing-expression", "'inputApiV2Event'"); - invoker = new FunctionInvoker(); - targetStream = new ByteArrayInputStream(this.apiGatewayV2Event.getBytes()); - output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - result = this.mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"Hello from Lambda\""); - } - - @Test - public void testResponseBase64Encoded() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoStringMessage"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - JsonMapper mapper = new JacksonMapper(new ObjectMapper()); - - String result = new String(output.toByteArray(), StandardCharsets.UTF_8); - Map resultMap = mapper.fromJson(result, Map.class); - assertThat((boolean) resultMap.get(AWSLambdaUtils.IS_BASE64_ENCODED)).isTrue(); - String body = new String(Base64.getDecoder().decode((String) resultMap.get(AWSLambdaUtils.BODY)), StandardCharsets.UTF_8); - assertThat(body).isEqualTo("hello"); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayAsSupplier() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "supply"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - System.out.println(result); - assertThat(result.get("body")).isEqualTo("\"boom\""); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testApiGatewayInAndOutInputStream() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoInputStreamToString"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("hello"); - Map headers = (Map) result.get("headers"); - assertThat(headers).isNotEmpty(); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testApiGatewayInAndOutInputStreamMsg() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "echoInputStreamMsgToString"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("hello"); - Map headers = (Map) result.get("headers"); - assertThat(headers).isNotEmpty(); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayInAndOut() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputOutputApiEvent"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("hello"); - Map headers = (Map) result.get("headers"); - assertThat(headers.get("foo")).isEqualTo("bar"); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayInAndOutV2() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputOutputApiEventV2"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("hello"); - Map headers = (Map) result.get("headers"); - assertThat(headers.get("foo")).isEqualTo("bar"); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayEventAsMessage() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputApiEventAsMessage"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - System.out.println(result); - assertThat(result.get("body")).isEqualTo("\"hello\""); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayEventAsMap() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "inputApiEventAsMap"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - System.out.println(result); - assertThat(result.get("body")).isEqualTo("\"hello\""); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayEventConsumer() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "consume"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"OK\""); - } - - @SuppressWarnings("rawtypes") - @Test - public void testApiGatewayWithMonoVoidAsReturn() throws Exception { - System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName()); - System.setProperty("spring.cloud.function.definition", "reactiveWithVoidReturn"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"OK\""); - } - - @Test - public void testWithDefaultRoutingFailure() throws Exception { - System.setProperty("MAIN_CLASS", SampleConfiguration.class.getName()); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - - try { - invoker.handleRequest(targetStream, output, null); - fail(); - } - catch (Exception e) { - // TODO: handle exception - } - } - - @SuppressWarnings("rawtypes") - @Test - public void testWithDefaultRouting() throws Exception { - System.setProperty("MAIN_CLASS", SampleConfiguration.class.getName()); - System.setProperty("spring.cloud.function.routing-expression", "'reverse'"); - FunctionInvoker invoker = new FunctionInvoker(); - - InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEvent.getBytes()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - invoker.handleRequest(targetStream, output, null); - - Map result = mapper.readValue(output.toByteArray(), Map.class); - assertThat(result.get("body")).isEqualTo("\"olleh\""); - } - - @EnableAutoConfiguration - @Configuration - public static class AuthorizerConfiguration { - @Bean - public Function acceptAuthorizerEvent() { - return v -> v.toString(); - } - } - - @EnableAutoConfiguration - @Configuration - public static class DynamoDbConfiguration { - @Bean - public Consumer consume() { - return event -> event.getRecords().forEach(System.out::println); - } - } - - @EnableAutoConfiguration - @Configuration - public static class SampleConfiguration { - @Bean - public Function echoString() { - return v -> v; - } - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - @Bean - public Function reverse() { - return v -> new StringBuilder(v).reverse().toString(); - } - - @Bean - public Function, Flux> echoStringReactive() { - return v -> v; - } - - @Bean - public Function, Flux> echoPojoReactive() { - return v -> v; - } - } - - @EnableAutoConfiguration - @Configuration - public static class KinesisConfiguration { - @Bean - public Function echoString() { - return v -> v; - } - - @Bean - public Function inputKinesisEvent() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - - @Bean - public Function, String> inputKinesisEventAsMessage() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - - @Bean - public Function, String> inputKinesisEventAsMap() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - } - - @EnableAutoConfiguration - @Configuration - public static class SQSConfiguration { - @Bean - public Function echoString() { - return v -> { - System.out.println("Echo: " + v); - return v.toString(); - }; - } - - @Bean - public Function inputSQSEvent() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - - @Bean - public Function, String> inputSQSEventAsMessage() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - - @Bean - public Function, String> inputSQSEventAsMap() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - - @Bean - public MyCustomMessageConverter messageConverter() { - return new MyCustomMessageConverter(); - } - } - - public static class MyCustomMessageConverter extends AbstractMessageConverter { - - public MyCustomMessageConverter() { - super(new MimeType("*", "*")); - } - - @Override - protected boolean supports(Class clazz) { - return (Person.class.equals(clazz)); - } - - @Override - protected Object convertFromInternal(Message message, Class targetClass, Object conversionHint) { - Object payload = message.getPayload(); - String v = payload instanceof String ? (String) payload : new String((byte[]) payload); - Person person = new Person(); - person.setName(v.substring(0, 10)); - return person; - } - } - - @EnableAutoConfiguration - @Configuration - public static class SNSConfiguration { - @Bean - public Function echoString() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - - @Bean - public Function inputSNSEvent() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - - @Bean - public Function, String> inputSNSEventAsMessage() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - - @Bean - public Function, String> inputSNSEventAsMap() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - } - - @EnableAutoConfiguration - @Configuration - public static class S3Configuration { - @Bean - public Function echoString() { - return v -> v; - } - - @Bean - public Function, Flux> echoStringFlux() { - return v -> v; - } - - @Bean - public Function inputS3Event(JsonMapper jsonMapper) { - return v -> { - System.out.println("Received: " + v); - return v.getRecords().get(0).getEventName(); - }; - } - - @Bean - public Function, String> inputS3EventAsMessage(JsonMapper jsonMapper) { - return m -> { - System.out.println("Received: " + m); - return m.getPayload().getRecords().get(0).getEventName(); - }; - } - - @Bean - public Function, String> inputS3EventAsMap() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - } - - @EnableAutoConfiguration - @Configuration - public static class LBConfiguration { - @Bean - public Function echoString() { - return v -> v; - } - - @Bean - public Function inputLBEvent() { - return v -> { - System.out.println("Received: " + v); - return v.getBody(); - }; - } - - @Bean - public Function inputOutputLBEvent() { - return v -> { - ApplicationLoadBalancerResponseEvent response = new ApplicationLoadBalancerResponseEvent(); - response.setBody(v.getBody()); - return response; - }; - } - - @Bean - public Function, String> inputLBEventAsMessage(JsonMapper jsonMapper) { - return message -> { - System.out.println("Received: " + message); - assertThat(message.getHeaders().get(AWSLambdaUtils.AWS_CONTEXT)).isNotNull(); - return message.getPayload().getBody(); - }; - } - - @Bean - public Function, String> inputLBEventAsMap() { - return v -> { - System.out.println("Received: " + v); - return v.toString(); - }; - } - } - - @EnableAutoConfiguration - @Configuration - public static class ApiGatewayConfiguration { - - @Bean - public Supplier supply() { - return () -> "boom"; - } - - @Bean - public Function, Message> echoStringMessage() { - return m -> { - String encodedPayload = Base64.getEncoder().encodeToString(m.getPayload().getBytes(StandardCharsets.UTF_8)); - return MessageBuilder.withPayload(encodedPayload).setHeader("isBase64Encoded", true).build(); - }; - } - - - @Bean - public Consumer consume() { - return v -> System.out.println(v); - } - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - @Bean - public Function, Mono> reactiveWithVoidReturn() { - return v -> Mono.empty(); - } - - @Bean - public Function uppercasePojo() { - return v -> { - return v.getName().toUpperCase(); - }; - } - - @Bean - public Function uppercasePojoReturnPojo() { - return v -> { - Person p = new Person(); - p.setName(v.getName().toUpperCase()); - return p; - }; - } - - @Bean - public Function, Flux> uppercasePojoReturnPojoReactive() { - return flux -> flux.map(v -> { - Person p = new Person(); - p.setName(v.getName().toUpperCase()); - return p; - }); - } - - @Bean - public Function inputApiEvent() { - return v -> { - return v.getBody(); - }; - } - - @Bean - - public Function echoInputStreamToString() { - return is -> { - try { - String result = StreamUtils.copyToString(is, StandardCharsets.UTF_8); - return result; - } - catch (Exception e) { - throw new RuntimeException(e); - } - }; - } - - @Bean - - public Function, String> echoInputStreamMsgToString() { - return msg -> { - try { - String result = StreamUtils.copyToString(msg.getPayload(), StandardCharsets.UTF_8); - return result; - } - catch (Exception e) { - throw new RuntimeException(e); - } - }; - } - - @Bean - public Function inputOutputApiEvent() { - return v -> { - APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent(); - response.setBody(v.getBody()); - response.setStatusCode(200); - response.setHeaders(Collections.singletonMap("foo", "bar")); - return response; - }; - } - - @Bean - public Function inputOutputApiEventV2() { - return v -> { - APIGatewayV2HTTPResponse response = new APIGatewayV2HTTPResponse(); - response.setBody(v.getBody()); - response.setStatusCode(200); - response.setHeaders(Collections.singletonMap("foo", "bar")); - return response; - }; - } - - @Bean - public Function inputOutputApiEventException() { - return v -> { - throw new IllegalStateException("Intentional"); - }; - } - - @Bean - public Function inputApiV2Event() { - return v -> { - return v.getBody(); - }; - } - - @Bean - public Function, String> inputApiEventAsMessage() { - return v -> { - return v.getPayload().getBody(); - }; - } - - @Bean - public Function, String> inputApiEventAsMap() { - return v -> { - String body = (String) v.get("body"); - return body; - }; - } - } - - public static class Person { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - return this.name; - } - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/TestContext.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/TestContext.java deleted file mode 100644 index 6af6b78f7..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/TestContext.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2019-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.aws; - -import com.amazonaws.services.lambda.runtime.ClientContext; -import com.amazonaws.services.lambda.runtime.CognitoIdentity; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.LambdaLogger; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class TestContext implements Context { - - @Override - public String getAwsRequestId() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getLogGroupName() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getLogStreamName() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getFunctionName() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getFunctionVersion() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getInvokedFunctionArn() { - // TODO Auto-generated method stub - return null; - } - - @Override - public CognitoIdentity getIdentity() { - // TODO Auto-generated method stub - return null; - } - - @Override - public ClientContext getClientContext() { - // TODO Auto-generated method stub - return null; - } - - @Override - public int getRemainingTimeInMillis() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int getMemoryLimitInMB() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public LambdaLogger getLogger() { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/README.adoc b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/README.adoc deleted file mode 100644 index 87dee4f61..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/README.adoc +++ /dev/null @@ -1,11 +0,0 @@ -=== Introduction - -Light weight Azure Function forwarding proxy which can deploy any existing Spring Boot web application as Azure Functions. -Infernally uses the Azure Http Trigger mapping. - -This module is identified as the only additional dependency to the existing web-app. - -A sample is provided in https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-web[azure-web sample] - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/pom.xml deleted file mode 100644 index f65f14857..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/pom.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - 4.0.0 - spring-cloud-function-adapter-azure-web - jar - spring-cloud-function-adapter-azure-web - Azure Function Adapter for Spring Cloud Function - - org.springframework.cloud - spring-cloud-function-adapter-parent - 4.1.0-SNAPSHOT - - - UTF-8 - UTF-8 - 17 - 3.0.0 - 1.0.0 - - - - com.google.code.findbugs - annotations - 3.0.1 - - - org.springframework.cloud - spring-cloud-function-context - - - org.springframework.cloud - spring-cloud-function-serverless-web - - - org.springframework.boot - spring-boot-autoconfigure - - - com.microsoft.azure.functions - azure-functions-java-library - ${azure.functions.java.core.version} - - - - com.microsoft.azure.functions - azure-functions-java-spi - ${azure.functions.java.spi.version} - - - jakarta.servlet - jakarta.servlet-api - provided - - - - com.fasterxml.jackson.core - jackson-databind - - - org.springframework - spring-webmvc - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-web - test - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvoker.java deleted file mode 100644 index 08c501152..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvoker.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.web; - -import java.util.Map.Entry; -import java.util.Optional; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.HttpResponseMessage.Builder; -import com.microsoft.azure.functions.HttpStatus; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.function.serverless.web.ProxyHttpServletRequest; -import org.springframework.cloud.function.serverless.web.ProxyHttpServletResponse; -import org.springframework.cloud.function.serverless.web.ProxyMvc; -import org.springframework.cloud.function.utils.FunctionClassUtils; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * - * @author Christian Tzolov - * @author Oleg Zhurakousky - * - */ -public class AzureWebProxyInvoker implements FunctionInstanceInjector { - - private static Log logger = LogFactory.getLog(AzureWebProxyInvoker.class); - - private static final String AZURE_WEB_ADAPTER_NAME = "AzureWebAdapter"; - private static final String AZURE_WEB_ADAPTER_ROUTE = AZURE_WEB_ADAPTER_NAME - + "/{e?}/{e2?}/{e3?}/{e4?}/{e5?}/{e6?}/{e7?}/{e8?}/{e9?}/{e10?}/{e11?}/{e12?}/{e13?}/{e14?}/{e15?}"; - - private ProxyMvc mvc; - - private ServletContext servletContext; - - @Override - public T getInstance(Class functionClass) throws Exception { - this.initialize(); - return (T) this; - } - - /** - * Because the getInstance is called by Azure Java Function on every function request we need to cache the Spring - * context initialization on the first function call. - * @throws ServletException error. - */ - private void initialize() throws ServletException { - synchronized (AzureWebProxyInvoker.class.getName()) { - if (mvc == null) { - Class startClass = FunctionClassUtils.getStartClass(); - this.mvc = ProxyMvc.INSTANCE(startClass); - } - } - } - - private HttpServletRequest prepareRequest(HttpRequestMessage> request) { - - int pathOffset = request.getUri().getPath().indexOf(AZURE_WEB_ADAPTER_NAME) + AZURE_WEB_ADAPTER_NAME.length(); - - String path = request.getUri().getPath().substring(pathOffset); - - ProxyHttpServletRequest httpRequest = new ProxyHttpServletRequest(servletContext, - request.getHttpMethod().toString(), path); - - request.getBody().ifPresent(body -> { - httpRequest.setContent(body.getBytes()); - }); - - if (!CollectionUtils.isEmpty(request.getQueryParameters())) { - httpRequest.setParameters(request.getQueryParameters()); - } - - if (!CollectionUtils.isEmpty(request.getHeaders())) { - for (Entry entry : request.getHeaders().entrySet()) { - httpRequest.addHeader(entry.getKey(), entry.getValue()); - } - } - - return httpRequest; - } - - @FunctionName(AZURE_WEB_ADAPTER_NAME) - public HttpResponseMessage execute( - @HttpTrigger(name = "req", methods = { HttpMethod.GET, - HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS, route = AZURE_WEB_ADAPTER_ROUTE) HttpRequestMessage> request, - ExecutionContext context) { - - context.getLogger().info("Request body is: " + request.getBody().orElse("[empty]")); - - HttpServletRequest httpRequest = this.prepareRequest(request); - - ProxyHttpServletResponse httpResponse = new ProxyHttpServletResponse(); - try { - this.mvc.service(httpRequest, httpResponse); - - Builder responseBuilder = request.createResponseBuilder(HttpStatus.OK); - for (String headerName : httpResponse.getHeaderNames()) { - responseBuilder.header(headerName, httpResponse.getHeader(headerName)); - } - - String responseString = httpResponse.getContentAsString(); - if (StringUtils.hasText(responseString)) { - if (logger.isDebugEnabled()) { - logger.debug("Response: " + responseString); - } - responseBuilder.body(responseString); - } // TODO: what to do with bodyless response? - - return responseBuilder.build(); - } - catch (Exception e) { - e.printStackTrace(); - throw new IllegalStateException(e); - } - - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/resources/META-INF/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/resources/META-INF/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector deleted file mode 100644 index e01d42baf..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/resources/META-INF/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector +++ /dev/null @@ -1 +0,0 @@ -org.springframework.cloud.function.adapter.azure.web.AzureWebProxyInvoker \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/resources/application.properties b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/resources/application.properties deleted file mode 100644 index d6705c881..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.banner.location=classpath:/spring-azure-function-banner.txt diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/resources/spring-azure-function-banner.txt b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/resources/spring-azure-function-banner.txt deleted file mode 100644 index 32225b776..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/main/resources/spring-azure-function-banner.txt +++ /dev/null @@ -1,8 +0,0 @@ - ____ _ _ _____ _ _ - / ___| _ __ _ __(_)_ __ __ _ / \ _____ _ _ __ ___ | ___| _ _ __ ___| |_(_) ___ _ __ ___ - \___ \| '_ \| '__| | '_ \ / _` | / _ \ |_ / | | | '__/ _ \ | |_ | | | | '_ \ / __| __| |/ _ \| '_ \/ __| - ___) | |_) | | | | | | | (_| | / ___ \ / /| |_| | | | __/ | _|| |_| | | | | (__| |_| | (_) | | | \__ \ - |____/| .__/|_| |_|_| |_|\__, | /_/ \_\/___|\__,_|_| \___| |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/ - |_| |___/ -${application.title} ${application.version} -Powered by Spring Boot ${spring-boot.version} and Azure Functions \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvokerTests.java deleted file mode 100644 index 55d79eab1..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/AzureWebProxyInvokerTests.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.web; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.HttpResponseMessage.Builder; -import com.microsoft.azure.functions.HttpStatus; -import com.microsoft.azure.functions.HttpStatusType; -import org.junit.jupiter.api.Test; - -public class AzureWebProxyInvokerTests { - - @Test - public void test() throws Exception { - System.setProperty("MAIN_CLASS", PetStoreSpringAppConfig.class.getName()); - AzureWebProxyInvoker proxyInvoker = new AzureWebProxyInvoker(); - AzureWebProxyInvoker instance = proxyInvoker.getInstance(AzureWebProxyInvoker.class); - - HttpRequestMessageStub> request = new HttpRequestMessageStub>(); - - request.setHttpMethod(HttpMethod.GET); - - request.setUri(new URI( - "http://localhost:7072/api/AzureWebAdapter/pets")); - - request.setBody(Optional.of("{\"id\":\"535932f1-d18b-488a-ad8f-8d50b9678492\"" + - "\"breed\":\"Beagle\",\"name\":\"Murphy\",\"dateOfBirth\":1591682824313}")); - - HttpResponseMessage response = instance.execute(request, new TestExecutionContext("execute")); - - System.out.println(response.getBody()); - - } - - public static class HttpRequestMessageStub implements HttpRequestMessage { - - private URI uri; - private HttpMethod httpMethod; - private Map headers; - private Map queryParameters; - private I body; - - public void setUri(URI uri) { - this.uri = uri; - } - - public void setHttpMethod(HttpMethod httpMethod) { - this.httpMethod = httpMethod; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public void setQueryParameters(Map queryParameters) { - this.queryParameters = queryParameters; - } - - public void setBody(I body) { - this.body = body; - } - - @Override - public URI getUri() { - return this.uri; - } - - @Override - public HttpMethod getHttpMethod() { - return this.httpMethod; - } - - @Override - public Map getHeaders() { - return this.headers; - } - - @Override - public Map getQueryParameters() { - return this.queryParameters; - } - - @Override - public I getBody() { - return this.body; - } - - @Override - public HttpResponseMessage.Builder createResponseBuilder(HttpStatusType status) { - return new BuilderStub().status(status); - } - - @Override - public Builder createResponseBuilder(HttpStatus status) { - return new BuilderStub().status(status); - } - - } - - public static class BuilderStub implements Builder { - - private HttpStatusType status; - private Map headers = new HashMap<>(); - private Object body; - - @Override - public Builder status(HttpStatusType status) { - this.status = status; - return this; - } - - @Override - public Builder header(String key, String value) { - headers.put(key, value); - return this; - } - - @Override - public Builder body(Object body) { - this.body = body; - return this; - } - - @Override - public HttpResponseMessage build() { - return new HttpResponseMessageStub(this.status, this.headers, this.body); - } - - } - - public static class HttpResponseMessageStub implements HttpResponseMessage { - - private HttpStatusType status; - private Map headers = new HashMap<>(); - private Object body; - - HttpResponseMessageStub(HttpStatusType status, Map headers, - Object body) { - this.status = status; - this.headers = headers; - this.body = body; - } - - @Override - public HttpStatusType getStatus() { - return this.status; - } - - @Override - public String getHeader(String key) { - return this.headers.get(key); - } - - @Override - public Object getBody() { - return this.body; - } - - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/Pet.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/Pet.java deleted file mode 100644 index 1db16446f..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/Pet.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.web; - -import java.util.Date; - -public class Pet { - private String id; - private String breed; - private String name; - private Date dateOfBirth; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getBreed() { - return breed; - } - - public void setBreed(String breed) { - this.breed = breed; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Date getDateOfBirth() { - return dateOfBirth; - } - - public void setDateOfBirth(Date dateOfBirth) { - this.dateOfBirth = dateOfBirth; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetData.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetData.java deleted file mode 100644 index 59989bf8a..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetData.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.web; - - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -public class PetData { - private static List breeds = new ArrayList<>(); - static { - breeds.add("Afghan Hound"); - breeds.add("Beagle"); - breeds.add("Bernese Mountain Dog"); - breeds.add("Bloodhound"); - breeds.add("Dalmatian"); - breeds.add("Jack Russell Terrier"); - breeds.add("Norwegian Elkhound"); - } - - private static List names = new ArrayList<>(); - static { - names.add("Bailey"); - names.add("Bella"); - names.add("Max"); - names.add("Lucy"); - names.add("Charlie"); - names.add("Molly"); - names.add("Buddy"); - names.add("Daisy"); - names.add("Rocky"); - names.add("Maggie"); - names.add("Jake"); - names.add("Sophie"); - names.add("Jack"); - names.add("Sadie"); - names.add("Toby"); - names.add("Chloe"); - names.add("Cody"); - names.add("Bailey"); - names.add("Buster"); - names.add("Lola"); - names.add("Duke"); - names.add("Zoe"); - names.add("Cooper"); - names.add("Abby"); - names.add("Riley"); - names.add("Ginger"); - names.add("Harley"); - names.add("Roxy"); - names.add("Bear"); - names.add("Gracie"); - names.add("Tucker"); - names.add("Coco"); - names.add("Murphy"); - names.add("Sasha"); - names.add("Lucky"); - names.add("Lily"); - names.add("Oliver"); - names.add("Angel"); - names.add("Sam"); - names.add("Princess"); - names.add("Oscar"); - names.add("Emma"); - names.add("Teddy"); - names.add("Annie"); - names.add("Winston"); - names.add("Rosie"); - } - - public static List getBreeds() { - return breeds; - } - - public static List getNames() { - return names; - } - - public static String getRandomBreed() { - return breeds.get(ThreadLocalRandom.current().nextInt(0, breeds.size() - 1)); - } - - public static String getRandomName() { - return names.get(ThreadLocalRandom.current().nextInt(0, names.size() - 1)); - } - - public static Date getRandomDoB() { - GregorianCalendar gc = new GregorianCalendar(); - - int year = ThreadLocalRandom.current().nextInt(Calendar.getInstance().get(Calendar.YEAR) - 15, - Calendar.getInstance().get(Calendar.YEAR)); - - gc.set(Calendar.YEAR, year); - - int dayOfYear = ThreadLocalRandom.current().nextInt(1, gc.getActualMaximum(Calendar.DAY_OF_YEAR)); - - gc.set(Calendar.DAY_OF_YEAR, dayOfYear); - return gc.getTime(); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetStoreSpringAppConfig.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetStoreSpringAppConfig.java deleted file mode 100644 index 7e8471ca9..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetStoreSpringAppConfig.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.web; - -import java.io.IOException; - -import jakarta.servlet.Filter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.web.servlet.HandlerAdapter; -import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - -@Configuration -@Import({ PetsController.class }) -public class PetStoreSpringAppConfig { - /* - * Create required HandlerMapping, to avoid several default HandlerMapping - * instances being created - */ - @Bean - public HandlerMapping handlerMapping() { - return new RequestMappingHandlerMapping(); - } - - /* - * Create required HandlerAdapter, to avoid several default HandlerAdapter - * instances being created - */ - @Bean - public HandlerAdapter handlerAdapter() { - return new RequestMappingHandlerAdapter(); - } - - @Bean - public Filter filter() { - return new Filter() { - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - System.out.println("FILTER ===> Hello from: " + request.getLocalAddr()); - chain.doFilter(request, response); - } - }; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetsController.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetsController.java deleted file mode 100644 index 7a99c53e8..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetsController.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.web; - -import java.security.Principal; -import java.util.Optional; -import java.util.UUID; - -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; - -@RestController -@EnableWebMvc -public class PetsController { - @RequestMapping(path = "/pets", method = RequestMethod.POST) - public Pet createPet(@RequestBody Pet newPet) { - if (newPet.getName() == null || newPet.getBreed() == null) { - return null; - } - - Pet dbPet = newPet; - dbPet.setId(UUID.randomUUID().toString()); - return dbPet; - } - - @RequestMapping(path = "/pets", method = RequestMethod.GET) - public Pet[] listPets(@RequestParam("limit") Optional limit, Principal principal) { - System.out.println("=====> EXECUTING"); - int queryLimit = 10; - if (limit.isPresent()) { - queryLimit = limit.get(); - } - - Pet[] outputPets = new Pet[queryLimit]; - - for (int i = 0; i < queryLimit; i++) { - Pet newPet = new Pet(); - newPet.setId(UUID.randomUUID().toString()); - newPet.setName(PetData.getRandomName()); - newPet.setBreed(PetData.getRandomBreed()); - newPet.setDateOfBirth(PetData.getRandomDoB()); - outputPets[i] = newPet; - } - - return outputPets; - } - - @RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET) - public Pet listPets() { - System.out.println("=====> Getting pet by id"); - Pet newPet = new Pet(); - newPet.setId(UUID.randomUUID().toString()); - newPet.setBreed(PetData.getRandomBreed()); - newPet.setDateOfBirth(PetData.getRandomDoB()); - newPet.setName(PetData.getRandomName()); - return newPet; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/TestExecutionContext.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/TestExecutionContext.java deleted file mode 100644 index bbdf32f56..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/TestExecutionContext.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.web; - -import java.util.UUID; -import java.util.logging.Logger; - -import com.microsoft.azure.functions.ExecutionContext; - -public class TestExecutionContext implements ExecutionContext { - - private String name; - - public TestExecutionContext(String name) { - this.name = name; - } - - @Override - public Logger getLogger() { - return Logger.getLogger(TestExecutionContext.class.getName()); - } - - @Override - public String getInvocationId() { - return UUID.randomUUID().toString(); - } - - @Override - public String getFunctionName() { - return this.name; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/.jdk8 b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/README.adoc b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/README.adoc deleted file mode 100644 index 74fbb3535..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/README.adoc +++ /dev/null @@ -1,17 +0,0 @@ -//// -DO NOT EDIT THIS FILE. IT WAS GENERATED. -Manual changes to this file will be lost when it is generated again. -Edit the files in the src/main/asciidoc/ directory instead. -//// - -This project provides an adapter layer for a Spring Cloud Function application onto Azure. -You can write an app with a single `@Bean` of type `Function` and it will be deployable in Azure if you get the JAR file laid out right. - -== Sample Functions - -- ../../spring-cloud-function-samples/function-sample-azure-http-trigger[Azure HTTP Trigger (Maven)] -- ../../spring-cloud-function-samples/function-sample-azure-http-trigger-gradle[Azure HTTP Trigger (Gradle)] -- ../../spring-cloud-function-samples/function-sample-azure-blob-trigger[Azure Blob Trigger (Maven)] -- ../../spring-cloud-function-samples/function-sample-azure-timer-trigger[Azure Timer Trigger (Maven)] -- ../../spring-cloud-function-samples/function-sample-azure-kafka-trigger[Azure Kafka Trigger & Output Binding (Maven)] -- ../../spring-cloud-function-samples/function-sample-azure/[(Legacy - FunctionInvoker) Azure HTTP Trigger (Maven)] diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/pom.xml deleted file mode 100644 index 0808fd70d..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/pom.xml +++ /dev/null @@ -1,110 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-adapter-azure - jar - - spring-cloud-function-adapter-azure - Azure Function Adapter for Spring Cloud Function - - - org.springframework.cloud - spring-cloud-function-adapter-parent - 4.1.0-SNAPSHOT - - - - UTF-8 - UTF-8 - 17 - - 3.0.0 - 1.0.0 - - - - - org.springframework.cloud - spring-cloud-function-context - - - org.springframework.boot - spring-boot-starter - - - spring-boot-starter-logging - org.springframework.boot - - - - - io.projectreactor - reactor-core - - - org.springframework.boot - spring-boot-starter-test - test - - - - com.microsoft.azure.functions - azure-functions-java-library - ${azure.functions.java.core.version} - - - - com.microsoft.azure.functions - azure-functions-java-spi - ${azure.functions.java.spi.version} - - - - org.slf4j - slf4j-log4j12 - 2.0.7 - test - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionInstanceInjector.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionInstanceInjector.java deleted file mode 100644 index 999d24c01..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionInstanceInjector.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure; - -import java.util.Map; - -import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.ResourceBanner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.WebApplicationType; -import org.springframework.cloud.function.utils.FunctionClassUtils; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.util.ClassUtils; -import org.springframework.util.CollectionUtils; - -/** - * The instance factory used by the Spring framework to initialize Azure function instance. It is waived with the Azure - * Java Worker through the META-INFO/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector service - * hook. The Azure Java Worker delegates scans the classpath for service definition and delegates the function class - * creation to this instance factory. - * @author Christian Tzolov - * @since 3.2.9 - */ -public class AzureFunctionInstanceInjector implements FunctionInstanceInjector { - - private static Log logger = LogFactory.getLog(AzureFunctionInstanceInjector.class); - - private static ConfigurableApplicationContext APPLICATION_CONTEXT; - - /** - * This method is called by the Azure Java Worker on every function invocation. The Worker sends in the classes - * annotated with @FunctionName annotations and allows the Spring framework to initialize the function instance as a - * Spring Bean and return the instance. Then the Worker uses the created instance to invoke the function. - * @param functionClass the class that contains customer Azure functions (e.g. @FunctionName annotated ) - * @param customer Azure functions class type - * @return the instance that will be invoked on by azure functions java worker - * @throws Exception any exception that is thrown by the Spring framework during instance creation - */ - @Override - public T getInstance(Class functionClass) throws Exception { - try { - // Backward compatibility workaround. If the function class is of type FunctionInvoker then create plain - // Java instance and delegate to FunctionInvoker adaptor approach. - if (ClassUtils.isAssignable(FunctionInvoker.class, functionClass)) { - return functionClass.getDeclaredConstructor().newInstance(); - } - - initialize(); - - Map azureFunctionBean = APPLICATION_CONTEXT.getBeansOfType(functionClass); - if (CollectionUtils.isEmpty(azureFunctionBean)) { - throw new IllegalStateException( - "Failed to retrieve Bean instance for: " + functionClass - + ". The class should be annotated with @Component to let the Spring framework initialize it!"); - } - return azureFunctionBean.entrySet().iterator().next().getValue(); - } - catch (Exception e) { - if (APPLICATION_CONTEXT != null) { - APPLICATION_CONTEXT.close(); - } - throw new IllegalStateException("Failed to initialize", e); - } - } - - /** - * Create a static Application Context instance shared between multiple function invocations. - */ - private static void initialize() { - synchronized (AzureFunctionInstanceInjector.class.getName()) { - if (APPLICATION_CONTEXT == null) { - Class springConfigurationClass = FunctionClassUtils.getStartClass(); - logger.info("Initializing: " + springConfigurationClass); - APPLICATION_CONTEXT = springApplication(springConfigurationClass).run(); - } - } - } - - private static SpringApplication springApplication(Class configurationClass) { - SpringApplication application = new org.springframework.cloud.function.context.FunctionalSpringApplication( - configurationClass); - application.setWebApplicationType(WebApplicationType.NONE); - application.setBanner(new ResourceBanner( - new DefaultResourceLoader().getResource("classpath:/spring-azure-function-banner.txt"))); - return application; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionUtil.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionUtil.java deleted file mode 100644 index 860e94a23..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureFunctionUtil.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; - -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; - -/** - * @author Christian Tzolov - * @author Oleg Zhurakousky - * @author Chris Bono - * @since 4.0 - */ -public final class AzureFunctionUtil { - - /** - * Message header key name used to store and extract the ExecutionContext. - */ - public static String EXECUTION_CONTEXT = "executionContext"; - - private AzureFunctionUtil() { - }; - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Object enhanceInputIfNecessary(Object input, ExecutionContext executionContext) { - if (input == null) { // Supplier - return input; - } - if (input instanceof Publisher) { - return Flux.from((Publisher) input).map(item -> { - if (item instanceof Message) { - return MessageBuilder.fromMessage((Message) item) - .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build(); - } - else { - return constructInputMessageFromItem(input, executionContext); - } - }); - } - else if (input instanceof Message) { - return MessageBuilder.fromMessage((Message) input) - .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build(); - } - else if (input instanceof Iterable) { - return Flux.fromIterable((Iterable) input).map(item -> { - return constructInputMessageFromItem(item, executionContext); - }); - } - return constructInputMessageFromItem(input, executionContext); - } - - private static Message constructInputMessageFromItem(Object input, ExecutionContext executionContext) { - MessageBuilder messageBuilder = null; - if (input instanceof HttpRequestMessage) { - HttpRequestMessage requestMessage = (HttpRequestMessage) input; - Object payload = requestMessage.getHttpMethod() != null - && requestMessage.getHttpMethod().equals(HttpMethod.GET) - ? requestMessage.getQueryParameters() - : requestMessage.getBody(); - - if (payload == null) { - payload = Optional.empty(); - } - messageBuilder = MessageBuilder.withPayload(payload).copyHeaders(getHeaders(requestMessage)); - } - else { - messageBuilder = MessageBuilder.withPayload(input); - } - return messageBuilder.setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build(); - } - - private static MessageHeaders getHeaders(HttpRequestMessage event) { - Map headers = new HashMap(); - - if (event.getHeaders() != null) { - headers.putAll(event.getHeaders()); - } - if (event.getQueryParameters() != null) { - headers.putAll(event.getQueryParameters()); - } - if (event.getUri() != null) { - headers.put("path", event.getUri().getPath()); - } - - if (event.getHttpMethod() != null) { - headers.put("httpMethod", event.getHttpMethod().toString()); - } - - headers.put("request", event.getBody()); - return new MessageHeaders(headers); - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java deleted file mode 100644 index decdd24c2..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage.Builder; -import com.microsoft.azure.functions.HttpStatus; -import com.microsoft.azure.functions.OutputBinding; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.WebApplicationType; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.config.FunctionContextUtils; -import org.springframework.cloud.function.context.config.JsonMessageConverter; -import org.springframework.cloud.function.context.config.SmartCompositeMessageConverter; -import org.springframework.cloud.function.json.JacksonMapper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.cloud.function.utils.FunctionClassUtils; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * @param input type - * @param result type - * @author Oleg Zhurakousky - * @author Chris Bono - * @author Christian Tzolov - * - * @since 3.2 - * - * @deprecated since 4.0.0 in favor of the dependency injection implementation {@link AzureFunctionInstanceInjector}. - * Follow the official documentation for further information. - */ -@Deprecated -public class FunctionInvoker { - - private static Log logger = LogFactory.getLog(FunctionInvoker.class); - - private static String EXECUTION_CONTEXT = "executionContext"; - - private static FunctionCatalog FUNCTION_CATALOG; - - private static ConfigurableApplicationContext APPLICATION_CONTEXT; - - private static JsonMapper OBJECT_MAPPER; - - public FunctionInvoker(Class configurationClass) { - try { - initialize(configurationClass); - } - catch (Exception e) { - this.close(); - throw new IllegalStateException("Failed to initialize", e); - } - } - - public FunctionInvoker() { - this(FunctionClassUtils.getStartClass()); - } - - public O handleRequest(ExecutionContext context) { - return this.handleRequest(null, context); - } - - public void close() { - FUNCTION_CATALOG = null; - } - - public void handleOutput(I input, OutputBinding binding, - ExecutionContext context) { - O result = handleRequest(input, context); - binding.setValue(result); - } - - private FunctionInvocationWrapper discoverFunction(String functionDefinition) { - FunctionInvocationWrapper function = FUNCTION_CATALOG.lookup(functionDefinition); - if (function != null && StringUtils.hasText(functionDefinition) - && !function.getFunctionDefinition().equals(functionDefinition)) { - this.registerFunction(functionDefinition); - function = FUNCTION_CATALOG.lookup(functionDefinition); - } - else if (function == null && StringUtils.hasText(functionDefinition) - && APPLICATION_CONTEXT.containsBean(functionDefinition)) { - this.registerFunction(functionDefinition); - function = FUNCTION_CATALOG.lookup(functionDefinition); - } - return function; - } - - public O handleRequest(I input, ExecutionContext executionContext) { - String functionDefinition = executionContext.getFunctionName(); - FunctionInvocationWrapper function = this.discoverFunction(functionDefinition); - Object enhancedInput = enhanceInputIfNecessary(input, executionContext); - - Object functionResult = function.apply(enhancedInput); - - if (functionResult instanceof Publisher) { - return postProcessReactiveFunctionResult(input, enhancedInput, (Publisher) functionResult, function, - executionContext); - } - return postProcessImperativeFunctionResult(input, enhancedInput, functionResult, function, executionContext); - } - - /** - * Post-processes the result from a non-reactive function invocation before returning it to the Azure runtime. The - * default behavior is to {@link #convertOutputIfNecessary possibly convert} the result. - * - *

- * Provides a hook for custom function invokers to extend/modify the function results handling. - * - * @param rawInputs the inputs passed in from the Azure runtime - * @param functionInputs the actual inputs used for the function invocation; may be {@link #enhanceInputIfNecessary - * different} from the {@literal rawInputs} - * @param functionResult the result from the function invocation - * @param function the invoked function - * @param executionContext the Azure execution context - * @return the possibly modified function results - */ - @SuppressWarnings("unchecked") - protected O postProcessImperativeFunctionResult(I rawInputs, Object functionInputs, Object functionResult, - FunctionInvocationWrapper function, ExecutionContext executionContext) { - return (O) this.convertOutputIfNecessary(rawInputs, functionResult); - } - - /** - * Post-processes the result from a reactive function invocation before returning it to the Azure runtime. The - * default behavior is to delegate to {@link #postProcessMonoFunctionResult} or - * {@link #postProcessFluxFunctionResult} based on the result type. - * - *

- * Provides a hook for custom function invokers to extend/modify the function results handling. - * - * @param rawInputs the inputs passed in from the Azure runtime - * @param functionInputs the actual inputs used for the function invocation; may be {@link #enhanceInputIfNecessary - * different} from the {@literal rawInputs} - * @param functionResult the result from the function invocation - * @param function the invoked function - * @param executionContext the Azure execution context - * @return the possibly modified function results - */ - protected O postProcessReactiveFunctionResult(I rawInputs, Object functionInputs, Publisher functionResult, - FunctionInvocationWrapper function, ExecutionContext executionContext) { - if (FunctionTypeUtils.isMono(function.getOutputType())) { - return postProcessMonoFunctionResult(rawInputs, functionInputs, Mono.from(functionResult), function, - executionContext); - } - return postProcessFluxFunctionResult(rawInputs, functionInputs, Flux.from(functionResult), function, - executionContext); - } - - /** - * Post-processes the {@code Mono} result from a reactive function invocation before returning it to the Azure - * runtime. The default behavior is to {@link Mono#blockOptional()} and {@link #convertOutputIfNecessary possibly - * convert} the result. - * - *

- * Provides a hook for custom function invokers to extend/modify the function results handling. - * - * @param rawInputs the inputs passed in from the Azure runtime - * @param functionInputs the actual inputs used for the function invocation; may be {@link #enhanceInputIfNecessary - * different} from the {@literal rawInputs} - * @param functionResult the Mono result from the function invocation - * @param function the invoked function - * @param executionContext the Azure execution context - * @return the possibly modified function results - */ - @SuppressWarnings("unchecked") - protected O postProcessMonoFunctionResult(I rawInputs, Object functionInputs, Mono functionResult, - FunctionInvocationWrapper function, ExecutionContext executionContext) { - return (O) this.convertOutputIfNecessary(rawInputs, functionResult.blockOptional().get()); - } - - /** - * Post-processes the {@code Flux} result from a reactive function invocation before returning it to the Azure - * runtime. The default behavior is to {@link Flux#toIterable() block} and {@link #convertOutputIfNecessary possibly - * convert} the results. - * - *

- * Provides a hook for custom function invokers to extend/modify the function results handling. - * - * @param rawInputs the inputs passed in from the Azure runtime - * @param functionInputs the actual inputs used for the function invocation; may be {@link #enhanceInputIfNecessary - * different} from the {@literal rawInputs} - * @param functionResult the Mono result from the function invocation - * @param function the invoked function - * @param executionContext the Azure execution context - * @return the possibly modified function results - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - protected O postProcessFluxFunctionResult(I rawInputs, Object functionInputs, Flux functionResult, - FunctionInvocationWrapper function, ExecutionContext executionContext) { - List resultList = new ArrayList<>(); - for (Object resultItem : functionResult.toIterable()) { - if (resultItem instanceof Collection) { - resultList.addAll((Collection) resultItem); - } - else { - if (!function.isSupplier() - && Collection.class.isAssignableFrom(FunctionTypeUtils.getRawType(function.getInputType())) - && !Collection.class.isAssignableFrom(FunctionTypeUtils.getRawType(function.getOutputType()))) { - return (O) this.convertOutputIfNecessary(rawInputs, resultItem); - } - else { - resultList.add(resultItem); - } - } - } - return (O) this.convertOutputIfNecessary(rawInputs, resultList); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void registerFunction(String functionDefinition) { - if (APPLICATION_CONTEXT.containsBean(functionDefinition)) { - FunctionRegistration functionRegistration = new FunctionRegistration( - APPLICATION_CONTEXT.getBean(functionDefinition), functionDefinition); - - Type type = FunctionContextUtils.findType(functionDefinition, APPLICATION_CONTEXT.getBeanFactory()); - - functionRegistration = functionRegistration.type(type); - - ((FunctionRegistry) FUNCTION_CATALOG).register(functionRegistration); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private Object enhanceInputIfNecessary(Object input, ExecutionContext executionContext) { - if (input == null) { // Supplier - return input; - } - if (input instanceof Publisher) { - return Flux.from((Publisher) input).map(item -> { - if (item instanceof Message) { - return MessageBuilder.fromMessage((Message) item) - .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build(); - } - else { - return constructInputMessageFromItem(input, executionContext); - } - }); - } - else if (input instanceof Message) { - return MessageBuilder.fromMessage((Message) input) - .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build(); - } - else if (input instanceof Iterable) { - return Flux.fromIterable((Iterable) input).map(item -> { - return constructInputMessageFromItem(item, executionContext); - }); - } - return constructInputMessageFromItem(input, executionContext); - } - - @SuppressWarnings("unchecked") - private Object convertOutputIfNecessary(Object input, Object output) { - if (input instanceof HttpRequestMessage) { - HttpRequestMessage requestMessage = (HttpRequestMessage) input; - Map headers = null; - if (output instanceof Message) { - headers = ((Message) output).getHeaders(); - output = ((Message) output).getPayload(); - } - Builder responseBuilder = requestMessage.createResponseBuilder(HttpStatus.OK).body(output); - if (headers != null) { - for (Entry headersEntry : headers.entrySet()) { - if (headersEntry.getValue() != null) { - responseBuilder.header(headersEntry.getKey(), headersEntry.getValue().toString()); - } - } - } - return responseBuilder.build(); - } - return output; - } - - @SuppressWarnings("unchecked") - private Message constructInputMessageFromItem(Object input, ExecutionContext executionContext) { - MessageBuilder messageBuilder = null; - if (input instanceof HttpRequestMessage) { - HttpRequestMessage requestMessage = (HttpRequestMessage) input; - Object payload = requestMessage.getHttpMethod() != null - && requestMessage.getHttpMethod().equals(HttpMethod.GET) - ? requestMessage.getQueryParameters() - : requestMessage.getBody(); - - if (payload == null) { - payload = Optional.empty(); - } - messageBuilder = MessageBuilder.withPayload(payload).copyHeaders(this.getHeaders(requestMessage)); - } - else { - messageBuilder = MessageBuilder.withPayload(input); - } - return messageBuilder.setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build(); - } - - private MessageHeaders getHeaders(HttpRequestMessage event) { - Map headers = new HashMap(); - - if (event.getHeaders() != null) { - headers.putAll(event.getHeaders()); - } - if (event.getQueryParameters() != null) { - headers.putAll(event.getQueryParameters()); - } - if (event.getUri() != null) { - headers.put("path", event.getUri().getPath()); - } - - if (event.getHttpMethod() != null) { - headers.put("httpMethod", event.getHttpMethod().toString()); - } - - headers.put("request", event.getBody()); - return new MessageHeaders(headers); - } - - private static void initialize(Class configurationClass) { - synchronized (FunctionInvoker.class.getName()) { - if (FUNCTION_CATALOG == null) { - logger.info("Initializing: " + configurationClass); - SpringApplication builder = springApplication(configurationClass); - APPLICATION_CONTEXT = builder.run(); - - Map mf = APPLICATION_CONTEXT.getBeansOfType(FunctionCatalog.class); - if (CollectionUtils.isEmpty(mf)) { - OBJECT_MAPPER = new JacksonMapper(new ObjectMapper()); - JsonMessageConverter jsonConverter = new JsonMessageConverter(OBJECT_MAPPER); - SmartCompositeMessageConverter messageConverter = new SmartCompositeMessageConverter( - Collections.singletonList(jsonConverter)); - FUNCTION_CATALOG = new SimpleFunctionRegistry( - APPLICATION_CONTEXT.getBeanFactory().getConversionService(), - messageConverter, OBJECT_MAPPER); - } - else { - OBJECT_MAPPER = APPLICATION_CONTEXT.getBean(JsonMapper.class); - FUNCTION_CATALOG = mf.values().iterator().next(); - } - } - } - - } - - private static SpringApplication springApplication(Class configurationClass) { - SpringApplication application = new org.springframework.cloud.function.context.FunctionalSpringApplication( - configurationClass); - application.setWebApplicationType(WebApplicationType.NONE); - return application; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvoker.java deleted file mode 100644 index c2af61fb3..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvoker.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure; - -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; - - -/** - * Implementation of HTTP Request Handler for Azure which supports - * HttpRequestMessage and HttpResponseMessage the types required by - * Azure Functions for HTTP-triggered functions. - * - * @param input type - * @author Oleg Zhurakousky - * - * @since 3.2 - * - * @deprecated since 4.0.0 in favor of the dependency injection implementation {@link AzureFunctionInstanceInjector}. - * Follow the official documentation for further information. - */ -@Deprecated -public class HttpFunctionInvoker extends - FunctionInvoker, HttpResponseMessage> { - - public HttpFunctionInvoker(Class configurationClass) { - super(configurationClass); - } - - public HttpFunctionInvoker() { - super(); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/resources/META-INF/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/resources/META-INF/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector deleted file mode 100644 index 247910e32..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/resources/META-INF/services/com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector +++ /dev/null @@ -1 +0,0 @@ -org.springframework.cloud.function.adapter.azure.AzureFunctionInstanceInjector \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/resources/spring-azure-function-banner.txt b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/resources/spring-azure-function-banner.txt deleted file mode 100644 index 32225b776..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/resources/spring-azure-function-banner.txt +++ /dev/null @@ -1,8 +0,0 @@ - ____ _ _ _____ _ _ - / ___| _ __ _ __(_)_ __ __ _ / \ _____ _ _ __ ___ | ___| _ _ __ ___| |_(_) ___ _ __ ___ - \___ \| '_ \| '__| | '_ \ / _` | / _ \ |_ / | | | '__/ _ \ | |_ | | | | '_ \ / __| __| |/ _ \| '_ \/ __| - ___) | |_) | | | | | | | (_| | / ___ \ / /| |_| | | | __/ | _|| |_| | | | | (__| |_| | (_) | | | \__ \ - |____/| .__/|_| |_|_| |_|\__, | /_/ \_\/___|\__,_|_| \___| |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/ - |_| |___/ -${application.title} ${application.version} -Powered by Spring Boot ${spring-boot.version} and Azure Functions \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java deleted file mode 100644 index 2299007cc..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure; - -import java.util.Arrays; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; - -import com.microsoft.azure.functions.ExecutionContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.cloud.function.adapter.azure.helper.TestExecutionContext; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.support.GenericMessage; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.util.Lists.list; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -/** - * Unit tests for {@link FunctionInvoker} custom result handling. - * - * @author Chris Bono - */ -class CustomFunctionInvokerTests { - - private FunctionInvoker currentInvoker; - - @AfterEach - void closeCurrentInvoker() { - if (this.currentInvoker != null) { - this.currentInvoker.close(); - } - } - - /** - * Verifies custom result handling and proper post-process callback invocation for an imperative function. - */ - @Test - void customImperativeResultHandling() { - FunctionInvoker invoker = new FunctionInvoker(TestFunctionsConfig.class) { - @Override - protected String postProcessImperativeFunctionResult(String rawInputs, Object functionInputs, - Object functionResult, FunctionInvocationWrapper function, ExecutionContext executionContext - ) { - return functionResult + "+imperative"; - } - }; - invoker = spyOnAndCloseAfterTest(invoker); - ExecutionContext executionContext = new TestExecutionContext("imperativeUppercase"); - String result = invoker.handleRequest("foo", executionContext); - assertThat(result).isEqualTo("FOO+imperative"); - - // Below here verifies that the expected callback(s) were invoked w/ the expected arguments - - // Only imperative post-process callback should be called - verify(invoker, never()).postProcessReactiveFunctionResult(anyString(), any(), any(Publisher.class), any(), same(executionContext)); - verify(invoker, never()).postProcessMonoFunctionResult(anyString(), any(), any(Mono.class), any(), same(executionContext)); - verify(invoker, never()).postProcessFluxFunctionResult(anyString(), any(), any(Flux.class), any(), same(executionContext)); - - // Only sniff-test the payload of the input message (the other fields are problematic to verify and no value doing that here) - ArgumentCaptor functionInputsCaptor = ArgumentCaptor.forClass(GenericMessage.class); - verify(invoker).postProcessImperativeFunctionResult(eq("foo"), functionInputsCaptor.capture(), eq("FOO"), any(), same(executionContext)); - assertThat(functionInputsCaptor.getValue()).extracting(GenericMessage::getPayload).isEqualTo("foo"); - } - - /** - * Verifies custom result handling and proper post-process callback invocation for a reactive Mono function. - */ - @Test - void customReactiveMonoResultHandling() { - FunctionInvoker invoker = new FunctionInvoker(TestFunctionsConfig.class) { - @Override - protected String postProcessMonoFunctionResult(String rawInputs, Object functionInputs, Mono functionResult, - FunctionInvocationWrapper function, ExecutionContext executionContext - ) { - return functionResult.block().toString() + "+mono"; - } - }; - invoker = spyOnAndCloseAfterTest(invoker); - ExecutionContext executionContext = new TestExecutionContext("reactiveMonoUppercase"); - String result = invoker.handleRequest("foo", executionContext); - assertThat(result).isEqualTo("FOO+mono"); - - // Below here verifies that the expected callback(s) were invoked w/ the expected arguments - - // Only publisher->mono post-process callbacks should be called - verify(invoker, never()).postProcessImperativeFunctionResult(anyString(), any(), any(), any(), same(executionContext)); - verify(invoker, never()).postProcessFluxFunctionResult(anyString(), any(), any(Flux.class), any(), same(executionContext)); - - // Only sniff-test the payload of the input message and the mono (the other fields are problematic to verify and no value doing that here) - ArgumentCaptor functionInputsCaptor = ArgumentCaptor.forClass(GenericMessage.class); - ArgumentCaptor functionResultCaptor = ArgumentCaptor.forClass(Mono.class); - verify(invoker).postProcessReactiveFunctionResult(eq("foo"), functionInputsCaptor.capture(), functionResultCaptor.capture(), any(), same(executionContext)); - verify(invoker).postProcessMonoFunctionResult(eq("foo"), functionInputsCaptor.capture(), functionResultCaptor.capture(), any(), same(executionContext)); - // NOTE: The captors get called twice as the args are just delegated from publisher->mono callback - assertThat(functionInputsCaptor.getAllValues()).extracting(GenericMessage::getPayload).containsExactly("foo", "foo"); - assertThat(functionResultCaptor.getAllValues()).extracting(Mono::block).containsExactly("FOO", "FOO"); - } - - /** - * Verifies custom result handling and proper post-process callback invocation for a reactive Flux function. - */ - @Test - void customReactiveFluxResultHandling() { - FunctionInvoker, String> invoker = new FunctionInvoker, String>(TestFunctionsConfig.class) { - @Override - protected String postProcessFluxFunctionResult(List rawInputs, Object functionInputs, - Flux functionResult, FunctionInvocationWrapper function, ExecutionContext executionContext - ) { - return functionResult.map(o -> o.toString() + "+flux").collectList().block().stream().collect(Collectors.joining("/")); - } - }; - invoker = spyOnAndCloseAfterTest(invoker); - ExecutionContext executionContext = new TestExecutionContext("reactiveFluxUppercase"); - List rawInputs = Arrays.asList("foo", "bar"); - String result = invoker.handleRequest(rawInputs, executionContext); - assertThat(result).isEqualTo("FOO+flux/BAR+flux"); - - // Below here verifies that the expected callback(s) were invoked w/ the expected arguments - - // Only publisher->flux post-process callbacks should be called - verify(invoker, never()).postProcessImperativeFunctionResult(anyList(), any(), any(), any(), same(executionContext)); - verify(invoker, never()).postProcessMonoFunctionResult(anyList(), any(), any(Mono.class), any(), same(executionContext)); - - // Only sniff-test the payload of the input message and the mono (the other fields are problematic to verify and no value doing that here) - ArgumentCaptor> functionInputsCaptor = ArgumentCaptor.forClass(Flux.class); - ArgumentCaptor functionResultCaptor = ArgumentCaptor.forClass(Flux.class); - verify(invoker).postProcessReactiveFunctionResult(same(rawInputs), functionInputsCaptor.capture(), functionResultCaptor.capture(), any(), same(executionContext)); - verify(invoker).postProcessFluxFunctionResult(same(rawInputs), functionInputsCaptor.capture(), functionResultCaptor.capture(), any(), same(executionContext)); - - // NOTE: The captors get called twice as the args are just delegated from publisher->flux callback - - // The functionInputs for each call is Flux with 2 items - one for 'foo' and one for 'bar' - assertThat(functionInputsCaptor.getAllValues()) - .extracting(Flux::collectList).extracting(Mono::block) - .flatExtracting(fluxAsList -> fluxAsList.stream().collect(Collectors.toList())) - .extracting(GenericMessage::getPayload).containsExactlyInAnyOrder("foo", "bar", "foo", "bar"); - - // The functionResult for each call is a Flux w/ 2 items { "FOO", "BAR" } - assertThat(functionResultCaptor.getAllValues()) - .extracting(Flux::collectList).extracting(Mono::block) - .containsExactlyInAnyOrder(list("FOO", "BAR"), list("FOO", "BAR")); - } - - private FunctionInvoker spyOnAndCloseAfterTest(FunctionInvoker invoker) { - this.currentInvoker = invoker; - return spy(invoker); - } - - @Configuration - @EnableAutoConfiguration - static class TestFunctionsConfig { - - @Bean - public Function imperativeUppercase() { - return (s) -> s.toUpperCase(); - } - - @Bean - public Function, Mono> reactiveMonoUppercase() { - return (m) -> m.map(String::toUpperCase); - } - - @Bean - public Function, Flux> reactiveFluxUppercase() { - return (f) -> f.map(String::toUpperCase); - } - - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java deleted file mode 100644 index a010af1d0..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import com.microsoft.azure.functions.ExecutionContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.cloud.function.adapter.azure.helper.TestExecutionContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; - -import static org.assertj.core.api.Assertions.assertThat; - - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public class FunctionInvokerTests { - - private FunctionInvoker handler = null; - - FunctionInvoker handler(Class config) { - FunctionInvoker handler = new FunctionInvoker( - config); - this.handler = handler; - return handler; - } - -// @Test // this is wrong too since function is Flux, Flux and while input may be single value, the output can still be multiple - public void bareConfig() { - FunctionInvoker handler = handler(BareConfig.class); - Bar bar = handler.handleRequest(new Foo("bar"), - new TestExecutionContext("uppercase")); - assertThat(bar.getValue()).isEqualTo("BAR"); - } - - @Test - public void autoConfig() { - FunctionInvoker handler = handler(AutoConfig.class); - Bar bar = handler.handleRequest(new Foo("bar"), - new TestExecutionContext("uppercase")); - assertThat(bar.getValue()).isEqualTo("BAR"); - } - - @Test - public void multiConfig() { - FunctionInvoker handler = handler(MultiConfig.class); - Bar bar = handler.handleRequest(new Foo("bar"), - new TestExecutionContext("uppercase")); - assertThat(bar.getValue()).isEqualTo("BAR"); - } - - @Test - public void implicitListConfig() { - FunctionInvoker, List> handler = handler( - AutoConfig.class); - List bar = handler.handleRequest(Arrays.asList(new Foo("bar"), new Foo("baz")), - new TestExecutionContext("uppercase")); - assertThat(bar).hasSize(2); - assertThat(bar.get(0).getValue()).isEqualTo("BAR"); - assertThat(bar.get(1).getValue()).isEqualTo("BAZ"); - } - - @Test - public void listToListConfig() { - FunctionInvoker, List> handler = handler( - ListConfig.class); - List bar = handler.handleRequest( - Arrays.asList(new Foo("bar"), new Foo("baz")), - new TestExecutionContext("uppercase")); - assertThat(bar).hasSize(2); - assertThat(bar.get(0).getValue()).isEqualTo("BAR"); - } - - @Test - public void listToListSingleConfig() { - FunctionInvoker, List> handler = handler( - ListConfig.class); - List bar = handler.handleRequest(Arrays.asList(new Foo("bar")), - new TestExecutionContext("uppercase")); - assertThat(bar).hasSize(1); - assertThat(bar.get(0).getValue()).isEqualTo("BAR"); - } - - @Test - public void collectConfig() { - FunctionInvoker, Bar> handler = handler( - CollectConfig.class); - Bar bar = handler.handleRequest(Arrays.asList(new Foo("bar")), - new TestExecutionContext("uppercase")); - assertThat(bar.getValue()).isEqualTo("BAR"); - } - - @Test - public void functionNonFluxBean() { - FunctionInvoker handler = handler(NonFluxFunctionConfig.class); - Bar bar = handler.handleRequest(new Foo("bar"), new TestExecutionContext("function")); - assertThat(bar).isNotNull(); - } - - @Test - public void supplierNonFluxBean() { - FunctionInvoker> handler = handler(NonFluxSupplierConfig.class); - List result = handler.handleRequest(new TestExecutionContext("supplier")); - - assertThat(result).isNotEmpty(); - assertThat(result.toString()).isEqualTo("[foo1, foo2]"); - } - - @Test - public void supplierPublisherBean() { - FunctionInvoker handler = handler(ReactiveSupplierConfig.class); - Foo resultSingle = (Foo) handler.handleRequest(new TestExecutionContext("suppliermono")); - assertThat(resultSingle.getValue()).isEqualTo("hello"); - - List resultList = (List) handler.handleRequest(new TestExecutionContext("supplierflux")); - assertThat(resultList.size()).isEqualTo(2); - } - - private static String consumerResult; - - @Test - public void consumerNonFluxBean() { - FunctionInvoker handler = handler(NonFluxConsumerConfig.class); - Object result = handler.handleRequest("foo1", new TestExecutionContext("consumer")); - - assertThat(result).isNull(); - assertThat(consumerResult).isEqualTo("foo1"); - } - - @Test - public void testReactiveFunctions() { - FunctionInvoker handler = handler(ReactiveFunctionConfiguration.class); - String result = handler.handleRequest("hello", new TestExecutionContext("uppercaseMono")); - - System.out.println(result); - -// assertThat(result).isNull(); -// assertThat(consumerResult).isEqualTo("foo1"); - } - - @AfterEach - public void close() throws IOException { - if (this.handler != null) { - this.handler.close(); - } - } - - @Configuration - protected static class NonFluxFunctionConfig { - - @Bean - public Function function() { - return foo -> new Bar(); - } - - } - - @Configuration - @EnableAutoConfiguration - protected static class ReactiveFunctionConfiguration { - - @Bean - public Function, Flux> echoStream() { - return f -> f; - } - - @Bean - public Function, Mono> uppercaseMono() { - return f -> f.map(v -> v.toUpperCase()); - } - - } - - @Configuration - protected static class NonFluxSupplierConfig { - - @Bean - public Supplier> supplier() { - return () -> Arrays.asList("foo1", "foo2"); - } - - } - - @Configuration - protected static class NonFluxConsumerConfig { - - @Bean - public Consumer consumer() { - return (v) -> consumerResult = v; - } - - } - - @Configuration -// @EnableAutoConfiguration - protected static class ReactiveSupplierConfig { - - @Bean - public Supplier> suppliermono() { - return () -> Mono.just(new Foo("hello")); - } - - @Bean - public Supplier> supplierflux() { - return () -> Flux.just(new Foo("hello"), new Foo("bye")); - } - - } - - @Configuration - protected static class BareConfig { - - @Bean("uppercase") - public Function, Flux> function() { - return foos -> foos.map(foo -> new Bar(foo.getValue().toUpperCase())); - } - - } - - @Configuration - @EnableAutoConfiguration - protected static class AutoConfig { - - @Bean - public Function, Bar> uppercase() { - return message -> { - Foo foo = message.getPayload(); - ExecutionContext targetContext = (ExecutionContext) message.getHeaders().get("executionContext"); - targetContext.getLogger().info("Invoking 'uppercase' on " + foo.getValue()); - return new Bar(foo.getValue().toUpperCase()); - }; - } - - } - - @Configuration - @EnableAutoConfiguration - protected static class ListConfig { - - @Bean - public Function, List> uppercase() { - return foos -> { - List bars = foos.stream().map(foo -> new Bar(foo.getValue().toUpperCase())) - .collect(Collectors.toList()); - return bars; - }; - } - - } - - @Configuration - @EnableAutoConfiguration - protected static class CollectConfig { - - @Bean - public Function, Bar> uppercase() { - return foos -> new Bar(foos.stream().map(foo -> foo.getValue().toUpperCase()) - .collect(Collectors.joining(","))); - } - - } - - @Configuration - @EnableAutoConfiguration - protected static class MultiConfig { - - @Bean - public Function, Bar> uppercase() { - - return message -> { - ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext"); - Foo foo = message.getPayload(); - context.getLogger().info("Executing uppercase function"); - return new Bar(foo.getValue().toUpperCase()); - }; - } - - @Bean - public Function, Foo> lowercase() { - return message -> { - ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext"); - Bar bar = message.getPayload(); - context.getLogger().info("Executing lowercase function"); - return new Foo(bar.getValue().toLowerCase()); - }; - } - - } - -} - -class Foo { - - private String value; - - Foo() { - } - - Foo(String value) { - this.value = value; - } - - public String lowercase() { - return this.value.toLowerCase(); - } - - public String uppercase() { - return this.value.toUpperCase(); - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - -} - -class Bar { - - private String value; - - Bar() { - } - - Bar(String value) { - this.value = value; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java deleted file mode 100644 index 20445fdc4..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; - -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpResponseMessage; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import org.springframework.cloud.function.adapter.azure.helper.HttpRequestMessageStub; -import org.springframework.cloud.function.adapter.azure.helper.TestExecutionContext; -import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.GenericMessage; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Markus Gulden - */ -public class HttpFunctionInvokerTests { - - private HttpFunctionInvoker handler = null; - - HttpFunctionInvoker handler(Class config) { - HttpFunctionInvoker handler = new HttpFunctionInvoker( - config); - this.handler = handler; - return handler; - } - - @Test - public void testWithBody() { - HttpFunctionInvoker handler = handler( - FunctionMessageBodyConfig.class); - HttpRequestMessageStub request = new HttpRequestMessageStub(); - request.setBody(new Foo("foo")); - - HttpResponseMessage response = handler.handleRequest(request, - new TestExecutionContext("uppercase")); - - assertThat(response.getBody()).isInstanceOf(Bar.class); - assertThat(response.getStatusCode()).isEqualTo(200); - Bar body = (Bar) response.getBody(); - assertThat(body.getValue()).isEqualTo("FOO"); - } - - @Test - public void testWithRequestParameters() throws URISyntaxException { - HttpFunctionInvoker handler = handler( - FunctionMessageEchoReqParametersConfig.class); - HttpRequestMessageStub request = new HttpRequestMessageStub(); - request.setUri(new URI("http://localhost:8080/pathValue")); - request.setHeaders(Collections.singletonMap("test-header", "headerValue")); - request.setQueryParameters(Collections.singletonMap("query", "queryValue")); - request.setHttpMethod(HttpMethod.GET); - - HttpResponseMessage response = handler.handleRequest(request, - new TestExecutionContext("uppercase")); - - assertThat(response.getStatusCode()).isEqualTo(200); - assertThat(response.getHeader("path")).isEqualTo("/pathValue"); - assertThat(response.getHeader("query")).isEqualTo("queryValue"); - assertThat(response.getHeader("test-header")).isEqualTo("headerValue"); - Bar body = (Bar) response.getBody(); - assertThat(body.getValue()).isEqualTo("body"); - } - - @Test - public void testWithEmptyBody() { - HttpFunctionInvoker handler = handler( - FunctionMessageConsumerConfig.class); - HttpRequestMessageStub request = new HttpRequestMessageStub(); - - HttpResponseMessage response = handler.handleRequest(request, - new TestExecutionContext("uppercase")); - - assertThat(response.getStatusCode()).isEqualTo(200); - Bar body = (Bar) response.getBody(); - assertThat(body).isNull(); - } - - @AfterEach - public void close() throws IOException { - if (this.handler != null) { - this.handler.close(); - } - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class FunctionMessageBodyConfig { - - @Bean - public Function, Message> function() { - return (foo -> { - Map headers = new HashMap<>(); - return new GenericMessage<>( - new Bar(foo.getPayload().getValue().toUpperCase()), headers); - }); - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class FunctionMessageEchoReqParametersConfig { - - @Bean - public Function, Message> function() { - return (message -> { - Map headers = new HashMap<>(); - headers.put("path", message.getHeaders().get("path")); - headers.put("query", message.getHeaders().get("query")); - headers.put("test-header", message.getHeaders().get("test-header")); - headers.put("httpMethod", message.getHeaders().get("httpMethod")); - return new GenericMessage<>(new Bar("body"), headers); - }); - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class FunctionMessageConsumerConfig { - - @Bean - public Consumer> function() { - return (foo -> { - }); - } - - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java deleted file mode 100644 index 94cd39f6d..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.helper; - -import java.util.HashMap; -import java.util.Map; - -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.HttpResponseMessage.Builder; -import com.microsoft.azure.functions.HttpStatusType; - -public class BuilderStub implements Builder { - - private HttpStatusType status; - private Map headers = new HashMap<>(); - private Object body; - - @Override - public Builder status(HttpStatusType status) { - this.status = status; - return this; - } - - @Override - public Builder header(String key, String value) { - headers.put(key, value); - return this; - } - - @Override - public Builder body(Object body) { - this.body = body; - return this; - } - - @Override - public HttpResponseMessage build() { - return new HttpResponseMessageStub(this.status, this.headers, this.body); - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java deleted file mode 100644 index b7db2b7d1..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.helper; - -import java.net.URI; -import java.util.Map; - -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.HttpResponseMessage.Builder; -import com.microsoft.azure.functions.HttpStatus; -import com.microsoft.azure.functions.HttpStatusType; - -public class HttpRequestMessageStub implements HttpRequestMessage { - - private URI uri; - private HttpMethod httpMethod; - private Map headers; - private Map queryParameters; - private I body; - - public void setUri(URI uri) { - this.uri = uri; - } - - public void setHttpMethod(HttpMethod httpMethod) { - this.httpMethod = httpMethod; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public void setQueryParameters(Map queryParameters) { - this.queryParameters = queryParameters; - } - - public void setBody(I body) { - this.body = body; - } - - @Override - public URI getUri() { - return this.uri; - } - - @Override - public HttpMethod getHttpMethod() { - return this.httpMethod; - } - - @Override - public Map getHeaders() { - return this.headers; - } - - @Override - public Map getQueryParameters() { - return this.queryParameters; - } - - @Override - public I getBody() { - return this.body; - } - - @Override - public HttpResponseMessage.Builder createResponseBuilder(HttpStatusType status) { - return new BuilderStub().status(status); - } - - @Override - public Builder createResponseBuilder(HttpStatus status) { - return new BuilderStub().status(status); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java deleted file mode 100644 index ffd939071..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.helper; - -import java.util.HashMap; -import java.util.Map; - -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.HttpStatusType; - -public class HttpResponseMessageStub implements HttpResponseMessage { - - private HttpStatusType status; - private Map headers = new HashMap<>(); - private Object body; - - public HttpResponseMessageStub(HttpStatusType status, Map headers, - Object body) { - this.status = status; - this.headers = headers; - this.body = body; - } - - @Override - public HttpStatusType getStatus() { - return this.status; - } - - @Override - public String getHeader(String key) { - return this.headers.get(key); - } - - @Override - public Object getBody() { - return this.body; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/TestExecutionContext.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/TestExecutionContext.java deleted file mode 100644 index e7cb7934a..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/TestExecutionContext.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.helper; - -import java.util.UUID; -import java.util.logging.Logger; - -import com.microsoft.azure.functions.ExecutionContext; - -public class TestExecutionContext implements ExecutionContext { - - private String name; - - public TestExecutionContext(String name) { - this.name = name; - } - - @Override - public Logger getLogger() { - return Logger.getLogger(TestExecutionContext.class.getName()); - } - - @Override - public String getInvocationId() { - return UUID.randomUUID().toString(); - } - - @Override - public String getFunctionName() { - return this.name; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java deleted file mode 100644 index 7a52dee43..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.injector; - -import java.util.Optional; -import java.util.function.Function; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.function.adapter.azure.AzureFunctionInstanceInjector; -import org.springframework.cloud.function.adapter.azure.AzureFunctionUtil; -import org.springframework.cloud.function.adapter.azure.helper.HttpRequestMessageStub; -import org.springframework.cloud.function.adapter.azure.helper.TestExecutionContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.stereotype.Component; - -/** - * This is an example JUnit test for Azure Adapter. - * - * @author Christian Tzolov - */ -public class AzureFunctionInstanceInjectorTest { - - static ExecutionContext TEST_EXECUTION_CONTEXT = new TestExecutionContext("hello"); - - @Test - public void testFunctionInjector() throws Exception { - - // The SpringBootApplication class - System.setProperty("MAIN_CLASS", MySpringConfig.class.getName()); - - FunctionInstanceInjector injector = new AzureFunctionInstanceInjector(); - - // Emulates the Azure Function Java Runtime DI call - MyAzureFunction azureFunction = injector.getInstance(MyAzureFunction.class); - - Assertions.assertThat(azureFunction).isNotNull(); - - HttpRequestMessageStub> requestStub = new HttpRequestMessageStub>(); - - requestStub.setBody(Optional.of("payload")); - - String result = azureFunction.execute(requestStub, TEST_EXECUTION_CONTEXT); - - Assertions.assertThat(result).isEqualTo("PAYLOAD"); - - Assertions.assertThat(azureFunction).isInstanceOf(MyAzureFunction.class); - } - - @Component - public static class MyAzureFunction { - - @Autowired - private Function, String> uppercase; - - @FunctionName("hello") - public String execute( - @HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - - Message enhancedRequest = (Message) AzureFunctionUtil.enhanceInputIfNecessary( - request.getBody().get(), - context); - - return uppercase.apply(enhancedRequest); - } - } - - @Configuration - @ComponentScan - public static class MySpringConfig { - - @Bean - public Function, String> uppercaseBean() { - return message -> { - ExecutionContext context = (ExecutionContext) message.getHeaders() - .get(AzureFunctionUtil.EXECUTION_CONTEXT); - - Assertions.assertThat(context).isNotNull(); - Assertions.assertThat(context.getFunctionName()).isEqualTo("hello"); - - return message.getPayload().toUpperCase(); - }; - - } - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java deleted file mode 100644 index 0af13fcd7..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.azure.injector; - -import java.util.Iterator; -import java.util.Optional; -import java.util.ServiceLoader; -import java.util.function.Function; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter; -import org.springframework.cloud.function.adapter.azure.AzureFunctionInstanceInjector; -import org.springframework.cloud.function.adapter.azure.AzureFunctionUtil; -import org.springframework.cloud.function.adapter.azure.helper.HttpRequestMessageStub; -import org.springframework.cloud.function.adapter.azure.helper.TestExecutionContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.messaging.Message; -import org.springframework.stereotype.Component; - -/** - * @author Christian Tzolov - */ -public class FunctionInstanceInjectorServiceLoadingTest { - - static ExecutionContext executionContext = new TestExecutionContext("hello"); - - @Test - public void testFunctionInjector() throws Exception { - - FunctionInstanceInjector injector = initializeFunctionInstanceInjector(); - Assertions.assertThat(injector).isNotNull(); - Assertions.assertThat(injector).isInstanceOf(AzureFunctionInstanceInjector.class); - - System.setProperty("MAIN_CLASS", MyMainConfig.class.getName()); - - MyAzureTestFunction functionInstance = injector.getInstance(MyAzureTestFunction.class); - - HttpRequestMessageStub> request = new HttpRequestMessageStub>(); - - request.setBody(Optional.of("test")); - - String result = functionInstance.execute(request, executionContext); - - Assertions.assertThat(result).isEqualTo("TEST"); - - Assertions.assertThat(functionInstance).isNotNull(); - Assertions.assertThat(functionInstance).isInstanceOf(MyAzureTestFunction.class); - } - - private static FunctionInstanceInjector initializeFunctionInstanceInjector() { - FunctionInstanceInjector functionInstanceInjector = null; - ClassLoader prevContextClassLoader = Thread.currentThread().getContextClassLoader(); - try { - Iterator iterator = ServiceLoader.load(FunctionInstanceInjector.class).iterator(); - if (iterator.hasNext()) { - functionInstanceInjector = iterator.next(); - if (iterator.hasNext()) { - throw new RuntimeException( - "Customer function app has multiple FunctionInstanceInjector implementations"); - } - } - else { - throw new IllegalStateException("Failed to resolve the AzureFunctionInstanceInjector as java service!"); - } - } - finally { - Thread.currentThread().setContextClassLoader(prevContextClassLoader); - } - return functionInstanceInjector; - } - - @Configuration - @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)}) - public static class MyMainConfig { - - @Bean - public Function, String> uppercase() { - return message -> { - ExecutionContext context = (ExecutionContext) message.getHeaders() - .get(AzureFunctionUtil.EXECUTION_CONTEXT); - Assertions.assertThat(context).isNotNull(); - Assertions.assertThat(context.getFunctionName()).isEqualTo("hello"); - return message.getPayload().toUpperCase(); - }; - } - } - - @Component - public static class MyAzureTestFunction { - - @Autowired - private Function, String> uppercase; - - @FunctionName("ditest") - public String execute( - @HttpTrigger(name = "req", methods = { HttpMethod.GET, - HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - - Message enhancedRequest = (Message) AzureFunctionUtil.enhanceInputIfNecessary( - request.getBody().get(), - context); - return uppercase.apply(enhancedRequest); - } - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/resources/log4j.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/resources/log4j.xml deleted file mode 100644 index 55fad21dc..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/resources/log4j.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml deleted file mode 100644 index d16fe0ff8..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-adapter-gcp - Spring Cloud Function Adapter GCP - Spring Cloud Function Adapter for Google Cloud Functions - - - spring-cloud-function-adapter-parent - org.springframework.cloud - 4.1.0-SNAPSHOT - - - - UTF-8 - UTF-8 - 1.0.3 - 1.0.3 - - - - - com.google.cloud.functions - functions-framework-api - ${google.cloud.functions.api.version} - - - com.google.code.gson - gson - - - org.springframework.cloud - spring-cloud-function-context - - - - - org.springframework.boot - spring-boot-loader-tools - - - org.springframework.boot - spring-boot-loader - - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework - spring-web - test - - - com.google.cloud.functions.invoker - java-function-invoker - ${google.cloud.functions.invoker.version} - test - - - - com.github.blindpirate - junit5-capture-system-output-extension - 0.1.2 - test - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/FunctionInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/FunctionInvoker.java deleted file mode 100644 index 062787d84..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/FunctionInvoker.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp; - -import java.io.BufferedReader; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Map.Entry; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import com.google.cloud.functions.Context; -import com.google.cloud.functions.HttpFunction; -import com.google.cloud.functions.HttpRequest; -import com.google.cloud.functions.HttpResponse; -import com.google.cloud.functions.RawBackgroundFunction; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.WebApplicationType; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionalSpringApplication; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.cloud.function.utils.FunctionClassUtils; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.Assert; -import org.springframework.util.MimeTypeUtils; - -/** - * Implementation of {@link HttpFunction} and {@link RawBackgroundFunction} for Google - * Cloud Function (GCF). This is the Spring Cloud Function adapter for GCF HTTP and Raw - * Background function. - * - * @author Dmitry Solomakha - * @author Mike Eltsufin - * @author Oleg Zhurakousky - * @author Biju Kunjummen - * @since 3.0.4 - */ -public class FunctionInvoker implements HttpFunction, RawBackgroundFunction { - - private static final Log log = LogFactory.getLog(FunctionInvoker.class); - - /** - * Constant specifying Http Status Code. Accessible to users by calling 'FunctionInvoker.HTTP_STATUS_CODE' - */ - public static final String HTTP_STATUS_CODE = "statusCode"; - - private String functionName = ""; - - protected FunctionCatalog catalog; - - private FunctionInvocationWrapper functionWrapped; - - private ConfigurableApplicationContext context; - - public FunctionInvoker() { - this(FunctionClassUtils.getStartClass()); - } - - public FunctionInvoker(Class configurationClass) { - init(configurationClass); - } - - private void init(Class configurationClass) { - // Default to GSON if implementation not specified. - if (!System.getenv().containsKey(ContextFunctionCatalogAutoConfiguration.JSON_MAPPER_PROPERTY)) { - System.setProperty(ContextFunctionCatalogAutoConfiguration.JSON_MAPPER_PROPERTY, "gson"); - } - Thread.currentThread() // TODO: remove after upgrading to 1.0.0-alpha-2-rc5 - .setContextClassLoader(FunctionInvoker.class.getClassLoader()); - - log.info("Initializing: " + configurationClass); - SpringApplication springApplication = springApplication(configurationClass); - this.context = springApplication.run(); - this.catalog = this.context.getBean(FunctionCatalog.class); - initFunctionConsumerOrSupplierFromCatalog(); - } - - private Function, Message> lookupFunction() { - Function, Message> function = this.catalog.lookup(functionName, - MimeTypeUtils.APPLICATION_JSON.toString()); - Assert.notNull(function, "'function' with name '" + functionName + "' must not be null"); - return function; - } - - /** - * The implementation of a GCF {@link HttpFunction} that will be used as the entry - * point from GCF. - */ - @Override - public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception { - Function, Message> function = lookupFunction(); - - Message message = this.functionWrapped.getInputType() == Void.class || this.functionWrapped.getInputType() == null ? null - : MessageBuilder.withPayload(httpRequest.getReader()).copyHeaders(httpRequest.getHeaders()).build(); - - Message result = function.apply(message); - - if (result != null) { - MessageHeaders headers = result.getHeaders(); - httpResponse.setContentType(result.getHeaders().get(MessageHeaders.CONTENT_TYPE).toString()); - httpResponse.getWriter().write(new String(result.getPayload(), StandardCharsets.UTF_8)); - for (Entry header : headers.entrySet()) { - Object values = header.getValue(); - if (values instanceof Collection) { - String headerValue = ((Collection) values).stream().map(item -> item.toString()).collect(Collectors.joining(",")); - httpResponse.appendHeader(header.getKey(), headerValue); - } - else { - httpResponse.appendHeader(header.getKey(), header.getValue().toString()); - } - } - httpRequest.getContentType().ifPresent(contentType -> httpResponse.setContentType(contentType)); - - if (headers.containsKey(HTTP_STATUS_CODE)) { - if (headers.get(HTTP_STATUS_CODE) instanceof Integer) { - httpResponse.setStatusCode((int) headers.get(HTTP_STATUS_CODE)); - } - else { - log.warn("The statusCode should be an Integer value"); - } - } - } - } - - /** - * The implementation of a GCF {@link RawBackgroundFunction} that will be used as the - * entry point from GCF. - * - * @param json the payload. - * @param context event context. - * @since 3.0.5 - */ - @Override - public void accept(String json, Context context) { - Function, Message> function = lookupFunction(); - Message message = this.functionWrapped.getInputType() == Void.class ? null - : MessageBuilder.withPayload(json).setHeader("gcf_context", context).build(); - - Message result = function.apply(message); - - if (result != null) { - log.info("Dropping background function result: " + new String(result.getPayload())); - } - } - - private void initFunctionConsumerOrSupplierFromCatalog() { - String name = resolveName(Function.class); - this.functionWrapped = this.catalog.lookup(Function.class, name); - if (this.functionWrapped != null) { - this.functionName = name; - return; - } - name = resolveName(Consumer.class); - this.functionWrapped = this.catalog.lookup(Consumer.class, name); - if (this.functionWrapped != null) { - this.functionName = name; - return; - } - - name = resolveName(Supplier.class); - this.functionWrapped = this.catalog.lookup(Supplier.class, name); - if (this.functionWrapped != null) { - this.functionName = name; - return; - } - - // Default to Routing Function - this.functionWrapped = this.catalog.lookup(RoutingFunction.FUNCTION_NAME, "application/json"); - if (this.functionWrapped != null) { - this.functionName = RoutingFunction.FUNCTION_NAME; - } - - Assert.notNull(this.functionWrapped, "Couldn't resolve a handler function"); - } - - private String resolveName(Class type) { - if (System.getenv().containsKey("spring.cloud.function.definition")) { - return System.getenv("spring.cloud.function.definition"); - } - String functionName = this.context.getEnvironment().getProperty("function.name"); - if (functionName != null) { - return functionName; - } - else if (type.isAssignableFrom(Function.class)) { - return "function"; - } - else if (type.isAssignableFrom(Consumer.class)) { - return "consumer"; - } - else if (type.isAssignableFrom(Supplier.class)) { - return "supplier"; - } - throw new IllegalStateException("Unknown type " + type); - } - - private SpringApplication springApplication(Class configurationClass) { - SpringApplication application = new FunctionalSpringApplication(configurationClass); - application.setWebApplicationType(WebApplicationType.NONE); - return application; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java deleted file mode 100644 index f45839ab5..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp; - -import com.google.cloud.functions.Context; -import com.google.cloud.functions.HttpFunction; -import com.google.cloud.functions.HttpRequest; -import com.google.cloud.functions.HttpResponse; -import com.google.cloud.functions.RawBackgroundFunction; - -import org.springframework.boot.loader.JarLauncher; -import org.springframework.boot.loader.jar.JarFile; - -/** - * The launcher class written at the top-level of the output JAR to be deployed to - * Google Cloud Functions. This is the entry point to the function when run from JAR. - * - * @author Ray Tsang - * @author Daniel Zou - */ -public class GcfJarLauncher extends JarLauncher implements HttpFunction, RawBackgroundFunction { - - private final ClassLoader loader; - - private final Object delegate; - - public GcfJarLauncher() throws Exception { - JarFile.registerUrlProtocolHandler(); - - this.loader = createClassLoader(getClassPathArchivesIterator()); - - Class clazz = this.loader - .loadClass("org.springframework.cloud.function.adapter.gcp.FunctionInvoker"); - this.delegate = clazz.getConstructor().newInstance(); - } - @Override - public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception { - Thread.currentThread().setContextClassLoader(this.loader); - ((HttpFunction) delegate).service(httpRequest, httpResponse); - } - - @Override - public void accept(String json, Context context) throws Exception { - Thread.currentThread().setContextClassLoader(this.loader); - ((RawBackgroundFunction) delegate).accept(json, context); - } -} - diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java deleted file mode 100644 index 874e0c66a..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp.layout; - -import java.io.IOException; - -import org.springframework.boot.loader.tools.CustomLoaderLayout; -import org.springframework.boot.loader.tools.Layouts; -import org.springframework.boot.loader.tools.LoaderClassesWriter; -import org.springframework.cloud.function.adapter.gcp.GcfJarLauncher; - -/** - * A custom JAR Layout that writes GCF adapter classes to the top-level of the output JAR - * for deploying to GCF. - * - * @author Ray Tsang - * @author Daniel Zou - */ -public class GcfJarLayout extends Layouts.Jar implements CustomLoaderLayout { - - private static final String LAUNCHER_NAME = GcfJarLauncher.class.getCanonicalName(); - - @Override - public String getLauncherClassName() { - return LAUNCHER_NAME; - } - - @Override - public boolean isExecutable() { - return false; - } - - @Override - public void writeLoadedClasses(LoaderClassesWriter writer) throws IOException { - writer.writeLoaderClasses(); - - String jarName = LAUNCHER_NAME.replaceAll("\\.", "/") + ".class"; - writer.writeEntry( - jarName, GcfJarLauncher.class.getResourceAsStream("/" + jarName)); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java deleted file mode 100644 index 87eacef00..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp.layout; - -import java.io.File; - -import org.springframework.boot.loader.tools.Layout; -import org.springframework.boot.loader.tools.LayoutFactory; - -/** - * Factory boilerplate class that constructs {@link GcfJarLayout}. - * - * @author Ray Tsang - * @author Daniel Zou - */ -public class GcfJarLayoutFactory implements LayoutFactory { - - @Override - public Layout getLayout(File source) { - return new GcfJarLayout(); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/resources/META-INF/spring.factories b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 76392ffda..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.loader.tools.LayoutFactory=\ -org.springframework.cloud.function.adapter.gcp.layout.GcfJarLayoutFactory diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/Context.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/Context.java deleted file mode 100644 index 46e77e425..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/Context.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp; - -/** - * An immutable implementation of the Google Cloud Function - * {@link com.google.cloud.functions.Context} interface. - * - * @author Mike Eltsufin - * @since 3.0.5 - */ -public class Context implements com.google.cloud.functions.Context { - - private String eventId; - - private String timestamp; - - private String eventType; - - private String resource; - - public Context() { - } - - public Context(String eventId, String timestamp, String eventType, String resource) { - this.eventId = eventId; - this.timestamp = timestamp; - this.eventType = eventType; - this.resource = resource; - } - - @Override - public String eventId() { - return this.eventId; - - } - - @Override - public String timestamp() { - return this.timestamp; - - } - - @Override - public String eventType() { - return this.eventType; - - } - - @Override - public String resource() { - return this.resource; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerBackgroundTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerBackgroundTests.java deleted file mode 100644 index 29951e594..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerBackgroundTests.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp; - -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.github.blindpirate.extensions.CaptureSystemOutput; -import com.google.gson.Gson; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; - -import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.messaging.Message; -import org.springframework.messaging.converter.AbstractMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.support.MessageBuilder; - -/** - * Unit tests for the background functions adapter for Google Cloud Functions. - * - * @author Dmitry Solomakha - * @author Mike Eltsufin - */ -@CaptureSystemOutput -public class FunctionInvokerBackgroundTests { - - private static final Gson gson = new Gson(); - - private static final String DROPPED_LOG_PREFIX = "Dropping background function result: "; - - @Test - public void testHelloWorldSupplier_Background(CaptureSystemOutput.OutputCapture outputCapture) { - testBackgroundFunction(outputCapture, HelloWorldSupplier.class, null, "Hello World!", null, null); - } - @Test - public void testJsonInputFunction_Background(CaptureSystemOutput.OutputCapture outputCapture) { - testBackgroundFunction(outputCapture, JsonInputFunction.class, new IncomingRequest("hello"), - "Thank you for sending the message: hello", null, null); - } - - @Test - public void testJsonInputOutputFunction_Background(CaptureSystemOutput.OutputCapture outputCapture) { - testBackgroundFunction(outputCapture, JsonInputOutputFunction.class, new IncomingRequest("hello"), - new OutgoingResponse("Thank you for sending the message: hello"), null, null); - } - - @Test - public void testJsonInputConsumer(CaptureSystemOutput.OutputCapture outputCapture) { - testBackgroundFunction(outputCapture, JsonInputConsumer.class, new IncomingRequest("hello"), null, - "Thank you for sending the message: hello", null); - } - - @Test - public void testPubSubBackgroundFunction_PubSub(CaptureSystemOutput.OutputCapture outputCapture) { - PubSubMessage pubSubMessage = new PubSubMessage(); - pubSubMessage.setData("hello"); - testBackgroundFunction(outputCapture, PubsubBackgroundFunction.class, pubSubMessage, null, - "Thank you for sending the message: hello", "google.pubsub.topic.publish"); - } - - @Test - public void testPubSubBackgroundFunction_PubSubPayload(CaptureSystemOutput.OutputCapture outputCapture) { - PubSubMessage pubSubMessage = new PubSubMessage(); - IncomingRequest message = new IncomingRequest("Hello"); - pubSubMessage.setData(gson.toJson(message)); - testBackgroundFunction(outputCapture, PubsubBackgroundFunctionPayload.class, pubSubMessage, null, - "Thank you for sending the message: Hello", "google.pubsub.topic.publish"); - } - - @Test - public void testPubSubBackgroundFunction_StringMessage(CaptureSystemOutput.OutputCapture outputCapture) { - PubSubMessage pubSubMessage = new PubSubMessage(); - pubSubMessage.setMessageId("1234"); - pubSubMessage.setData("Hello"); - testBackgroundFunction(outputCapture, PubsubBackgroundFunctionStringMessage.class, pubSubMessage, null, - "Message: Hello; Type: google.pubsub.topic.publish; Message ID: 1234", "google.pubsub.topic.publish"); - } - - @Test - public void testPubSubBackgroundFunction_PubSubMessage(CaptureSystemOutput.OutputCapture outputCapture) { - PubSubMessage pubSubMessage = new PubSubMessage(); - pubSubMessage.setMessageId("1234"); - pubSubMessage.setData("Hello"); - testBackgroundFunction(outputCapture, PubsubBackgroundFunctionPubSubMessage.class, pubSubMessage, null, - "Message: Hello; Type: google.pubsub.topic.publish; Message ID: 1234", "google.pubsub.topic.publish"); - } - - private void testBackgroundFunction(CaptureSystemOutput.OutputCapture outputCapture, Class configurationClass, I input, O expectedResult, - String expectedSysOut, String eventType) { - - FunctionInvoker handler = new FunctionInvoker(configurationClass); - - handler.accept(gson.toJson(input), new Context(null, null, eventType, null)); - - // verify function sysout statements - if (expectedSysOut != null) { - outputCapture.expect(Matchers.containsString(expectedSysOut)); - } - - // verify that if function had a return type, it was logged as being dropped - if (expectedResult != null) { - outputCapture.expect(Matchers.containsString(DROPPED_LOG_PREFIX + gson.toJson(expectedResult))); - } - else { - outputCapture.expect(Matchers.not(Matchers.containsString(DROPPED_LOG_PREFIX))); - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class HelloWorldSupplier { - - @Bean - public Supplier supplier() { - return () -> "Hello World!"; - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class JsonInputFunction { - - @Bean - public Function function() { - return (in) -> "Thank you for sending the message: " + in.message; - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class JsonInputOutputFunction { - - @Bean - public Function> function() { - return (in) -> MessageBuilder - .withPayload(new OutgoingResponse("Thank you for sending the message: " + in.message)) - .setHeader("foo", "bar").build(); - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class JsonInputConsumer { - - @Bean - public Consumer function() { - return (in) -> System.out.println("Thank you for sending the message: " + in.message); - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class PubsubBackgroundFunction { - - @Bean - public Consumer consumer() { - return (in) -> System.out.println("Thank you for sending the message: " + in.getData()); - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class PubsubBackgroundFunctionPayload { - - @Bean - public Consumer consumerPayload() { - return (in) -> System.out.println("Thank you for sending the message: " + in.message); - } - - @Bean - public MessageConverter messageToIncomingRequestConverter(JsonMapper mapper) { - return new AbstractMessageConverter() { - - @Override - protected boolean supports(Class aClass) { - return aClass == IncomingRequest.class; - } - - @Override - protected Object convertFromInternal(Message message, Class targetClass, Object conversionHint) { - PubSubMessage pubSubMessage = mapper.fromJson(message.getPayload(), PubSubMessage.class); - return mapper.fromJson(pubSubMessage.getData(), IncomingRequest.class); - } - }; - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class PubsubBackgroundFunctionStringMessage { - - @Bean - public Consumer> consumeStringMessage(JsonMapper mapper) { - return (message) -> { - PubSubMessage pubSubMessage = mapper.fromJson(message.getPayload(), PubSubMessage.class); - String payload = pubSubMessage.getData(); - - String eventType = ((Context) message.getHeaders().get("gcf_context")).eventType(); - String messageId = pubSubMessage.getMessageId(); - System.out.println("Message: " + payload + "; Type: " + eventType + "; Message ID: " + messageId); - }; - } - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class PubsubBackgroundFunctionPubSubMessage { - - @Bean - public Consumer> consumePubSubMessage() { - return (message) -> { - String payload = message.getPayload().getData(); - String eventType = ((Context) message.getHeaders().get("gcf_context")).eventType(); - String messageId = message.getPayload().getMessageId(); - System.out.println("Message: " + payload + "; Type: " + eventType + "; Message ID: " + messageId); - }; - } - - } - - public static class IncomingRequest { - - String message; - - public IncomingRequest(String message) { - this.message = message; - } - - public IncomingRequest() { - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - } - - public static class OutgoingResponse { - - String message; - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - OutgoingResponse(String message) { - this.message = message; - } - - public OutgoingResponse() { - } - - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerHttpTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerHttpTests.java deleted file mode 100644 index c178414ae..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerHttpTests.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.google.cloud.functions.HttpRequest; -import com.google.cloud.functions.HttpResponse; -import com.google.gson.Gson; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; - -import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.boot.test.system.OutputCaptureExtension; -import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Unit tests for the HTTP functions adapter for Google Cloud Functions. - * - * @author Dmitry Solomakha - * @author Mike Eltsufin - */ - -@ExtendWith(OutputCaptureExtension.class) -public class FunctionInvokerHttpTests { - - private static final Gson gson = new Gson(); - private HttpRequest request; - private HttpResponse response; - private BufferedWriter bufferedWriter; - private StringWriter writer; - - @BeforeEach - void testSetup() throws IOException { - request = Mockito.mock(HttpRequest.class); - response = Mockito.mock(HttpResponse.class); - writer = new StringWriter(); - bufferedWriter = new BufferedWriter(writer); - when(response.getWriter()).thenReturn(bufferedWriter); - } - - @Test - public void testHelloWorldSupplier() throws Exception { - - String expectedOutput = "Hello World!"; - FunctionInvoker handler = new FunctionInvoker(HelloWorldSupplier.class); - - handler.service(request, response); - bufferedWriter.close(); - - - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); - - - } - - - @Test - public void testJsonInputFunction() throws Exception { - - FunctionInvoker handler = new FunctionInvoker(JsonInputFunction.class); - - String expectedOutput = "Thank you for sending the message: hello"; - IncomingRequest input = new IncomingRequest("hello"); - - when(request.getReader()).thenReturn(new BufferedReader(new StringReader(gson.toJson(input)))); - handler.service(request, response); - bufferedWriter.close(); - - - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); - } - - @Test - public void testWithKanji() throws Exception { - - FunctionInvoker handler = new FunctionInvoker(JsonInputFunction.class); - - String expectedOutput = "Thank you for sending the message: 森林"; - IncomingRequest input = new IncomingRequest("森林"); - - when(request.getReader()).thenReturn(new BufferedReader(new StringReader(gson.toJson(input)))); - handler.service(request, response); - bufferedWriter.close(); - - - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); - } - - @Test - public void testJsonInputOutputFunction() throws Exception { - - FunctionInvoker handler = new FunctionInvoker(JsonInputOutputFunction.class); - - OutgoingResponse expectedOutput = new OutgoingResponse("Thank you for sending the message: hello"); - IncomingRequest input = new IncomingRequest("hello"); - - when(request.getReader()).thenReturn(new BufferedReader(new StringReader(gson.toJson(input)))); - handler.service(request, response); - bufferedWriter.close(); - - - assertThat(writer.toString()).isEqualTo(gson.toJson(expectedOutput)); - - - } - - @Test - public void testJsonInputConsumer_Background(CapturedOutput capturedOutput) throws Exception { - - FunctionInvoker handler = new FunctionInvoker(JsonInputConsumer.class); - - IncomingRequest input = new IncomingRequest("hello"); - - when(request.getReader()).thenReturn(new BufferedReader(new StringReader(gson.toJson(input)))); - handler.service(request, response); - bufferedWriter.close(); - - assertThat(capturedOutput.toString()).contains("Thank you for sending the message: hello"); - - } - - @Test - public void testStatusCodeSet() throws Exception { - - FunctionInvoker handler = new FunctionInvoker(StatusCodeSupplier.class); - String input = "hello"; - when(request.getReader()).thenReturn(new BufferedReader(new StringReader(gson.toJson(input)))); - handler.service(request, response); - bufferedWriter.close(); - - verify(response).setStatusCode(404); - - } - - @Test - public void testMultiValueHeaderSupplied() throws Exception { - - FunctionInvoker handler = new FunctionInvoker(MultiValueHeaderSupplier.class); - String input = "hello"; - when(request.getReader()).thenReturn(new BufferedReader(new StringReader(gson.toJson(input)))); - handler.service(request, response); - bufferedWriter.close(); - - verify(response).appendHeader("multiValueHeader", "123,headerThing"); - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class HelloWorldSupplier { - - @Bean - public Supplier supplier() { - return () -> "Hello World!"; - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class StatusCodeSupplier { - - @Bean - public Function> function() { - - String payload = "hello"; - - Message msg = MessageBuilder.withPayload(payload).setHeader("statusCode", 404) - .build(); - - return x -> msg; - }; - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class MultiValueHeaderSupplier { - - @Bean - public Function> function() { - - String payload = "hello"; - List li = new ArrayList(asList(123, "headerThing")); - - Message msg = MessageBuilder.withPayload(payload).setHeader("multiValueHeader", li) - .build(); - - return x -> msg; - }; - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class JsonInputFunction { - - @Bean - public Function function() { - return (in) -> "Thank you for sending the message: " + in.message; - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class JsonInputOutputFunction { - - @Bean - public Function> function() { - return (in) -> { - return MessageBuilder - .withPayload(new OutgoingResponse("Thank you for sending the message: " + in.message)) - .setHeader("foo", "bar").build(); - }; - } - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - protected static class JsonInputConsumer { - - @Bean - public Consumer function() { - return (in) -> System.out.println("Thank you for sending the message: " + in.message); - } - - } - - public static class IncomingRequest { - - String message; - - public IncomingRequest(String message) { - this.message = message; - } - - public IncomingRequest() { - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - } - - public static class OutgoingResponse { - - String message; - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - OutgoingResponse(String message) { - this.message = message; - } - - public OutgoingResponse() { - } - - } - - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/PubSubMessage.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/PubSubMessage.java deleted file mode 100644 index aa1322348..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/PubSubMessage.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp; - -import java.util.Map; - -/** - * A class that can be mapped to the GCF Pub/Sub Message event type. This is for use in - * the background functions. - * - * @author Mike Eltsufin - * @since 3.0.5 - */ -public class PubSubMessage { - - private String data; - - private Map attributes; - - private String messageId; - - private String publishTime; - - public String getData() { - return data; - } - - public void setData(String data) { - this.data = data; - } - - public Map getAttributes() { - return attributes; - } - - public void setAttributes(Map attributes) { - this.attributes = attributes; - } - - public String getMessageId() { - return messageId; - } - - public void setMessageId(String messageId) { - this.messageId = messageId; - } - - public String getPublishTime() { - return publishTime; - } - - public void setPublishTime(String publishTime) { - this.publishTime = publishTime; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/FunctionInvokerIntegrationTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/FunctionInvokerIntegrationTests.java deleted file mode 100644 index 054de3b1f..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/FunctionInvokerIntegrationTests.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp.integration; - -import java.io.IOException; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.cloud.function.adapter.gcp.integration.LocalServerTestSupport.verify; - -/** - * Integration tests for GCF Http Functions. - * - * @author Daniel Zou - * @author Mike Eltsufin - */ -public class FunctionInvokerIntegrationTests { - - @Test - public void testSingular() { - verify(CloudFunctionMainSingular.class, null, "hello", "HELLO"); - } - - @Test - public void testUppercase() throws InterruptedException, IOException { - verify(CloudFunctionMain.class, "uppercase", "hello", "HELLO"); - - } - - @Test - public void testFooBar() { - verify(CloudFunctionMain.class, "foobar", new Foo("Hi"), new Bar("Hi")); - } - - @Test - public void testErrorResponse() { - try (LocalServerTestSupport.ServerProcess serverProcess = - LocalServerTestSupport.startServer(ErrorFunction.class, "errorFunction")) { - - TestRestTemplate testRestTemplate = new TestRestTemplate(); - - HttpHeaders headers = new HttpHeaders(); - ResponseEntity response = testRestTemplate.postForEntity( - "http://localhost:" + serverProcess.getPort(), new HttpEntity<>("test", headers), - String.class); - - assertThat(response.getStatusCode().is5xxServerError()).isTrue(); - } - catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * An example function which throws an error to test response code propagation. - */ - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - static class ErrorFunction { - - @Bean - Supplier errorFunction() { - return () -> { - throw new RuntimeException(); - }; - } - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - static class CloudFunctionMainSingular { - - @Bean - Function uppercase() { - return input -> input.toUpperCase(); - } - - } - - @Configuration - @Import({ ContextFunctionCatalogAutoConfiguration.class }) - static class CloudFunctionMain { - - @Bean - Function uppercase() { - return input -> input.toUpperCase(); - } - - @Bean - Function foobar() { - return input -> new Bar(input.value); - } - - } - - public static class Foo { - - String value; - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public Foo() { - } - - Foo(String value) { - this.value = value; - } - - } - - public static class Bar { - - String value; - - Bar(String value) { - this.value = value; - } - - Bar() { - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/LocalServerTestSupport.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/LocalServerTestSupport.java deleted file mode 100644 index aa98eca30..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/LocalServerTestSupport.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.adapter.gcp.integration; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import com.google.cloud.functions.invoker.runner.Invoker; -import com.google.gson.Gson; - -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.function.adapter.gcp.FunctionInvoker; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Test support class for running tests on the local Cloud Function server. - * - * @author Daniel Zou - * @author Mike Eltsufin - * @author Chris Bono - */ -final public class LocalServerTestSupport { - - private static final Gson gson = new Gson(); - - private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(); - - private static final String SERVER_READY_STRING = "Started ServerConnector"; - - private LocalServerTestSupport() { - } - - /** - * Starts up the Cloud Function Server and executes the test. - */ - public static void verify(Class mainClass, String function, I input, O expectedOutput) { - try (ServerProcess serverProcess = LocalServerTestSupport.startServer(mainClass, function)) { - TestRestTemplate testRestTemplate = new TestRestTemplate(); - - HttpHeaders headers = new HttpHeaders(); - - ResponseEntity response = testRestTemplate.postForEntity( - "http://localhost:" + serverProcess.getPort(), new HttpEntity<>(gson.toJson(input), headers), - String.class); - - assertThat(response.getBody()).isEqualTo(gson.toJson(expectedOutput)); - } - catch (Exception e) { - e.printStackTrace(); - } - } - - static ServerProcess startServer(Class springApplicationMainClass, String function) throws IOException { - String signatureType = "http"; - String target = FunctionInvoker.class.getCanonicalName(); - - File javaHome = new File(System.getProperty("java.home")); - assertThat(javaHome.exists()).isTrue(); - File javaBin = new File(javaHome, "bin"); - File javaCommand = new File(javaBin, "java"); - assertThat(javaCommand.exists()).isTrue(); - String myClassPath = System.getProperty("java.class.path"); - assertThat(myClassPath).isNotNull(); - - List command = new ArrayList<>(); - command.addAll(Arrays.asList(javaCommand.toString(), "-classpath", myClassPath, Invoker.class.getName())); - - ProcessBuilder processBuilder = new ProcessBuilder().command(command).redirectErrorStream(true); - Map environment = new HashMap<>(); - environment.put("PORT", String.valueOf(0)); - environment.put("K_SERVICE", "test-function"); - environment.put("FUNCTION_SIGNATURE_TYPE", signatureType); - environment.put("FUNCTION_TARGET", target); - environment.put("MAIN_CLASS", springApplicationMainClass.getCanonicalName()); - if (function != null) { - environment.put("spring.cloud.function.definition", function); - } - processBuilder.environment().putAll(environment); - Process serverProcess = processBuilder.start(); - Future outputMonitorResult = EXECUTOR.submit(() -> monitorOutput(serverProcess.getInputStream())); - - int port; - try { - port = outputMonitorResult.get(5L, TimeUnit.SECONDS); - } - catch (Exception ex) { - serverProcess.destroy(); - throw new AssertionError("Server never became ready"); - } - return new ServerProcess(serverProcess, port); - } - - private static Integer monitorOutput(InputStream processOutput) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(processOutput))) { - String line; - while ((line = reader.readLine()) != null) { - System.out.println(line); - if (line.contains(SERVER_READY_STRING)) { - // Started ServerConnector@192b07fd{HTTP/1.1,[http/1.1]}{0.0.0.0:59259} - String portStr = line.substring(line.lastIndexOf(':') + 1, line.lastIndexOf('}')); - return Integer.parseInt(portStr); - } - if (line.contains("WARNING")) { - throw new AssertionError("Found warning in server output:\n" + line); - } - } - } - catch (IOException e) { - throw new UncheckedIOException(e); - } - throw new RuntimeException("End of input stream and server never became ready"); - } - - static class ServerProcess implements AutoCloseable { - - private final Process process; - - private final int port; - - ServerProcess(Process process, int port) { - this.process = process; - this.port = port; - } - - Process process() { - return process; - } - - @Override - public void close() { - process().destroy(); - } - - public int getPort() { - return port; - } - - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.gitignore b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index e76d1f324..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55..000000000 Binary files a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index abd303b67..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/README.md b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/README.md deleted file mode 100644 index 803e84caa..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Spring Cloud Function gRPC extension to support CloudEvent proto. - -This extension project designed as an extension to general Spring Cloud Function gRPC support to specifically suport -[CloudEvent proto](https://github.com/cloudevents/spec/blob/v1.0.1/spec.proto) - -To use it simply import it as a dependency to your project together with - -```xml - - org.springframework.cloud - spring-cloud-function-grpc - -``` - -Your project should also explicitly import [CloudEvent proto](https://github.com/cloudevents/spec/blob/v1.0.1/spec.proto) and -service proto - -``` -syntax = "proto3"; - -package io.cloudevents.v1; - -import "google/protobuf/any.proto"; -import "google/protobuf/timestamp.proto"; -import "CloudEvent.proto"; - -service CloudEventService { - rpc biStream(stream io.cloudevents.v1.CloudEvent) returns (stream io.cloudevents.v1.CloudEvent); - - rpc clientStream(stream io.cloudevents.v1.CloudEvent) returns (io.cloudevents.v1.CloudEvent); - - rpc serverStream(io.cloudevents.v1.CloudEvent) returns (stream io.cloudevents.v1.CloudEvent); - - rpc requestReply(io.cloudevents.v1.CloudEvent) returns (io.cloudevents.v1.CloudEvent); -} -``` - -Once done, you can send/receive CloudEvent messages - -You can also reference [this sample](https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-grpc-cloudevent) diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/mvnw b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/mvnw deleted file mode 100755 index a16b5431b..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/mvnw.cmd b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/mvnw.cmd deleted file mode 100644 index c8d43372c..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/pom.xml deleted file mode 100644 index f0e8c7631..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/pom.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - 4.0.0 - - org.springframework.cloud - spring-cloud-function-adapter-parent - 4.1.0-SNAPSHOT - - spring-cloud-function-grpc-cloudevent-ext - spring-cloud-function-grpc-cloudevent-ext - CloudEvent extension for spring-cloud-function-grpc - - 1.55.1 - - - - org.springframework.boot - spring-boot-starter - - - org.springframework.cloud - spring-cloud-function-grpc - ${project.version} - - - io.grpc - grpc-stub - ${grpc.version} - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - kr.motd.maven - os-maven-plugin - 1.6.1 - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - 0.6.1 - - - com.google.protobuf:protoc:3.23.0:exe:${os.detected.classifier} - - grpc-java - - io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} - - - - - - compile - compile-custom - - - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - false - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - false - - - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventGrpcAutoConfiguration.java b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventGrpcAutoConfiguration.java deleted file mode 100644 index 65c381fc6..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventGrpcAutoConfiguration.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc.ce; - -import io.grpc.BindableService; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cloud.function.grpc.MessageHandlingHelper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * - * @author Oleg Zhurakousky - * - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnProperty(name = "spring.cloud.function.grpc.server", havingValue = "true", matchIfMissing = true) -public class CloudEventGrpcAutoConfiguration { - - @SuppressWarnings("rawtypes") - @Bean - public BindableService cloudEventMessageHandler(MessageHandlingHelper helper) { - return new CloudEventHandler(helper); - } - - @Bean - @ConditionalOnMissingBean - public CloudEventMessageConverter cloudEventMessageConverter() { - return new CloudEventMessageConverter(); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventHandler.java b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventHandler.java deleted file mode 100644 index 90be0bda1..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventHandler.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc.ce; - -import io.cloudevents.v1.CloudEventServiceGrpc.CloudEventServiceImplBase; -import io.cloudevents.v1.proto.CloudEvent; -import io.grpc.stub.StreamObserver; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.function.grpc.MessageHandlingHelper; - -/** - * - * @author Oleg Zhurakousky - * @since 3.2 - * - */ -@SuppressWarnings("rawtypes") -class CloudEventHandler extends CloudEventServiceImplBase { - - private Log logger = LogFactory.getLog(CloudEventHandler.class); - - private final MessageHandlingHelper helper; - - - - CloudEventHandler(MessageHandlingHelper helper) { - this.helper = helper; - } - - - @SuppressWarnings("unchecked") - @Override - public void requestReply(CloudEvent request, StreamObserver responseObserver) { - this.helper.requestReply(request, responseObserver); - } -} - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventMessageConverter.java b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventMessageConverter.java deleted file mode 100644 index b8bc95a72..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/java/org/springframework/cloud/function/grpc/ce/CloudEventMessageConverter.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc.ce; - -import java.util.Map.Entry; - -import com.google.protobuf.ByteString; -import com.google.protobuf.GeneratedMessageV3; -import io.cloudevents.v1.proto.CloudEvent; -import io.cloudevents.v1.proto.CloudEvent.Builder; -import io.cloudevents.v1.proto.CloudEvent.CloudEventAttributeValue; -import io.cloudevents.v1.proto.CloudEvent.CloudEventAttributeValue.AttrCase; - -import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils; -import org.springframework.cloud.function.grpc.AbstractGrpcMessageConverter; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class CloudEventMessageConverter extends AbstractGrpcMessageConverter { - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - protected Message doToSpringMessage(CloudEvent cloudEvent) { - MessageBuilder builder = MessageBuilder.withPayload(cloudEvent.getTextData()); - builder.setHeader(CloudEventMessageUtils.TYPE, cloudEvent.getType()); - builder.setHeader(CloudEventMessageUtils.SOURCE, cloudEvent.getSource()); - builder.setHeader(CloudEventMessageUtils.ID, cloudEvent.getId()); - builder.setHeader(CloudEventMessageUtils.SPECVERSION, cloudEvent.getId()); - - for (Entry attributeEntry : cloudEvent.getAttributesMap().entrySet()) { - AttrCase attrCase = attributeEntry.getValue().getAttrCase(); - if (attrCase.equals(AttrCase.CE_BOOLEAN)) { - builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeBoolean()); - } - else if (attrCase.equals(AttrCase.CE_BYTES)) { - builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeBytes()); - } - else if (attrCase.equals(AttrCase.CE_INTEGER)) { - builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeInteger()); - } - else if (attrCase.equals(AttrCase.CE_STRING)) { - builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeString()); - } - else if (attrCase.equals(AttrCase.CE_TIMESTAMP)) { - builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeTimestamp()); - } - else if (attrCase.equals(AttrCase.CE_URI)) { - builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeUri()); - } - else if (attrCase.equals(AttrCase.CE_URI_REF)) { - builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeUriRef()); - } - else { - throw new IllegalStateException("Unknown type for attribute " + attributeEntry.getKey()); - } - - } - return builder.build(); - } - - @Override - protected CloudEvent doFromSpringMessage(Message springMessage) { - Builder builder = CloudEvent.newBuilder() - .setTextDataBytes(ByteString.copyFrom(springMessage.getPayload())) - .setType(CloudEventMessageUtils.getType(springMessage)) - .setSource(CloudEventMessageUtils.getSource(springMessage).toString()) - .setId(CloudEventMessageUtils.getId(springMessage)) - .setSpecVersion(CloudEventMessageUtils.getSpecVersion(springMessage)); - - - for (Entry entry : springMessage.getHeaders().entrySet()) { - builder.putAttributes(entry.getKey(), CloudEventAttributeValue.newBuilder().setCeString(entry.getValue().toString()).build()); - } - return builder.build(); - - } - - @Override - protected boolean supports(Class grpcClass) { - return grpcClass.isAssignableFrom(CloudEvent.class); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/proto/CloudEvent.proto b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/proto/CloudEvent.proto deleted file mode 100644 index 7952c1f79..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/proto/CloudEvent.proto +++ /dev/null @@ -1,49 +0,0 @@ -syntax = "proto3"; - -package io.cloudevents.v1; - -import "google/protobuf/any.proto"; -import "google/protobuf/timestamp.proto"; - -option go_package = "cloudevents.io/genproto/v1"; -option java_package = "io.cloudevents.v1.proto"; -option java_multiple_files = true; - -message CloudEvent { - - // -- CloudEvent Context Attributes - - // Required Attributes - string id = 1; - string source = 2; // URI-reference - string spec_version = 3; - string type = 4; - - // Optional & Extension Attributes - map attributes = 5; - - // -- CloudEvent Data (Bytes, Text, or Proto) - oneof data { - bytes binary_data = 6; - string text_data = 7; - google.protobuf.Any proto_data = 8; - } - - /** - * The CloudEvent specification defines - * seven attribute value types... - */ - - message CloudEventAttributeValue { - - oneof attr { - bool ce_boolean = 1; - int32 ce_integer = 2; - string ce_string = 3; - bytes ce_bytes = 4; - string ce_uri = 5; - string ce_uri_ref = 6; - google.protobuf.Timestamp ce_timestamp = 7; - } - } -} \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/proto/CloudEventService.proto b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/proto/CloudEventService.proto deleted file mode 100644 index 1a7be6a74..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/proto/CloudEventService.proto +++ /dev/null @@ -1,17 +0,0 @@ -syntax = "proto3"; - -package io.cloudevents.v1; - -import "google/protobuf/any.proto"; -import "google/protobuf/timestamp.proto"; -import "CloudEvent.proto"; - -service CloudEventService { - rpc biStream(stream io.cloudevents.v1.CloudEvent) returns (stream io.cloudevents.v1.CloudEvent); - - rpc clientStream(stream io.cloudevents.v1.CloudEvent) returns (io.cloudevents.v1.CloudEvent); - - rpc serverStream(io.cloudevents.v1.CloudEvent) returns (stream io.cloudevents.v1.CloudEvent); - - rpc requestReply(io.cloudevents.v1.CloudEvent) returns (io.cloudevents.v1.CloudEvent); -} \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 4c9640b55..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -org.springframework.cloud.function.grpc.ce.CloudEventGrpcAutoConfiguration \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/resources/application.properties b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/resources/application.properties deleted file mode 100644 index 8b1378917..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/test/java/org/springframework/cloud/grpc/ce/SpringCloudFunctionGrpcCloudeventApplicationTests.java b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/test/java/org/springframework/cloud/grpc/ce/SpringCloudFunctionGrpcCloudeventApplicationTests.java deleted file mode 100644 index 8ab26e6bd..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/src/test/java/org/springframework/cloud/grpc/ce/SpringCloudFunctionGrpcCloudeventApplicationTests.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.grpc.ce; - -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class SpringCloudFunctionGrpcCloudeventApplicationTests { - -// @Test -// void contextLoads() { -// } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/.jdk8 b/spring-cloud-function-adapters/spring-cloud-function-grpc/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/README.md b/spring-cloud-function-adapters/spring-cloud-function-grpc/README.md deleted file mode 100644 index 57b6d7ce5..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/README.md +++ /dev/null @@ -1,233 +0,0 @@ -### Introduction - -Spring Cloud Function allows you to invoke function via [gRPC](https://grpc.io/). While you can read more about gRPC in te provided link, this section will describe the parts relevant to Spring Cloud Function integration. - -As with all other Spring-boot based frameworks all you need to do is add `spring-cloud-function-grpc` dependency to your POM. -```xml - - org.springframework.cloud - spring-cloud-function-grpc - ${current.version} - -``` - -### Programming model - -#### Two operation modes (client/server) -Spring Cloud Function gRPC support provides two modes of operation - _client_ and _server_. In other words when you add `spring-cloud-function-grpc` dependency to your POM you may or may not want the gRPC server as you may -only be interested in client-side utilities to invoke a function exposed via gRPC server running on some host/port. -To support these two modes Spring Cloud Function provides `spring.cloud.function.grpc.server` which defaults to `true`. -This means that the default mode of operation is _server_, since the core intention of our current gRPC support is to expose user Functions via gRPC. However, if you're only inteersted in using client-side utilities (e.g., `GrpcUtils` to help to invoke a function or convert `GrpcMessage` to Spring `Message` and vice versa), you can set this property to `false`. - -In the server (default) mode, te gRPC server would be bound to te default port ***6048***. You can change it by providing -`spring.cloud.function.grpc.port` property. - -#### Core Data and Service -At the center of gRPC and Spring Cloud Function integration is a canonical protobuff structure - `GrpcMessage`. It is modeled after Spring [Message](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/messaging/Message.html). - -``` -message GrpcSpringMessage { - bytes payload = 1; - map headers = 2; -} -``` -As you can see it is a very generic structure which can support any type of data amd metadata you wish to exchange. - -It also defines a `MessagingService` allowing you to generate required stubs to support true plolyglot nature of gRPC. - -``` -service MessagingService { - rpc biStream(stream GrpcSpringMessage) returns (stream GrpcSpringMessage); - - rpc clientStream(stream GrpcSpringMessage) returns (GrpcSpringMessage); - - rpc serverStream(GrpcSpringMessage) returns (stream GrpcSpringMessage); - - rpc requestReply(GrpcSpringMessage) returns (GrpcSpringMessage); -} -``` -That said, when using Java, you do not need to generate anything, rather identify function definition and send and receive Spring `Messages`. -You can get a pretty good idea from this [test case](https://github.com/spring-cloud/spring-cloud-function/blob/82e2583acd7c8aaaf2bc5ec935d486a336e97ae7/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java#L49). - -#### 4 Interaction RPC Modes - -The gRPC provides 4 interaction modes -* Reques/Repply RPC -* Server-side streaming RPC -* Client-side streaming RPC -* Bi-directional streaming RPC - -Spring Cloud Function provides support for all 4 of them. - -##### Request Reply RPC -The most straight forward interaction mode is _Request/Reply_. -Suppose you have a function - -```java -@EnableAutoConfiguration -public static class SampleConfiguration { - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } -} -``` -After identifying this function via `spring.cloud.function.definition` property (see example [here](https://github.com/spring-cloud/spring-cloud-function/blob/ded02fec0a6d3d66b8ec00f99f28be2a4bbec668/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java)), -you can invoke it using utility method(s) provided in `GrpcUtils` class - -```java -Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes()) - .setHeader("foo", "bar") - .build(); -Message reply = GrpcUtils.requestReply(message); -``` - -You can also provide `spring.cloud.function.definition` property via `Message` headers, to support more dynamic cases. - -```java -Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes()) - .setHeader("foo", "bar") - .setHeader("spring.cloud.function.definition", "reverse") - .build(); -``` - -##### Server-side streaming RPC -The Server-side streaming RPC allows you to reply with the stream of data. - -```java -@EnableAutoConfiguration -public static class SampleConfiguration { - @Bean - public Function> stringInStreamOut() { - return value -> Flux.just(value, value.toUpperCase()); - } -} -``` -After identifying this function via `spring.cloud.function.definition` property (see example [here](https://github.com/spring-cloud/spring-cloud-function/blob/ded02fec0a6d3d66b8ec00f99f28be2a4bbec668/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java)), -you can invoke it using utility method(s) provided in `GrpcUtils` class - -```java -Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes()).setHeader("foo", "bar").build(); - -Flux> reply = - GrpcUtils.serverStream("localhost", FunctionGrpcProperties.GRPC_PORT, message); - -List> results = reply.collectList().block(Duration.ofSeconds(5)); -``` - -You can see that gRPC stream is mapped to instance of `Flux` from [project reactor](https://projectreactor.io/) - -Similarly to the _request/reply_ you can also provide `spring.cloud.function.definition` property via `Message` headers, to support more dynamic cases. - -```java -Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes()) - .setHeader("foo", "bar") - .setHeader("spring.cloud.function.definition", "reverse") - .build(); -``` - -##### Client-side streaming RPC -The Client-side streaming RPC allows you to stream input data and receive a single reply. - -```java -@EnableAutoConfiguration -public static class SampleConfiguration { - @Bean - public Function, String> streamInStringOut() { - return flux -> flux.doOnNext(v -> { - try { - // do something useful - Thread.sleep(new Random().nextInt(2000)); // artificial delay - } - catch (Exception e) { - // ignore - } - }).collectList().block().toString(); - } -} -``` -After identifying this function via `spring.cloud.function.definition` property (see example [here](https://github.com/spring-cloud/spring-cloud-function/blob/ded02fec0a6d3d66b8ec00f99f28be2a4bbec668/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java)), -you can invoke it using utility method(s) provided in `GrpcUtils` class - -```java -List> messages = new ArrayList<>(); -messages.add(MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar") - .build()); -messages.add(MessageBuilder.withPayload("\"Julien\"".getBytes()).setHeader("foo", "bar") - .build()); -messages.add(MessageBuilder.withPayload("\"Bubbles\"".getBytes()).setHeader("foo", "bar") - .build()); - -Message reply = - GrpcUtils.clientStream("localhost", FunctionGrpcProperties.GRPC_PORT, Flux.fromIterable(messages)); - -``` - -You can see that gRPC stream is mapped to instance of `Flux` from [project reactor](https://projectreactor.io/) - -Unlike the _request/reply_ and _server-side streaming_, you can ONLY pass function definition via property or environment variable. - -##### Bi-Directional streaming RPC -The bi-directional streaming RPC allows you to stream input and output data. - -```java -@EnableAutoConfiguration -public static class SampleConfiguration { - @Bean - public Function, Flux> uppercaseReactive() { - return flux -> flux.map(v -> v.toUpperCase()); - } -} -``` -After identifying this function via `spring.cloud.function.definition` property (see example [here](https://github.com/spring-cloud/spring-cloud-function/blob/ded02fec0a6d3d66b8ec00f99f28be2a4bbec668/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java)), -you can invoke it using utility method(s) provided in `GrpcUtils` class - -```java -List> messages = new ArrayList<>(); -messages.add(MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar") - .build()); -messages.add(MessageBuilder.withPayload("\"Julien\"".getBytes()).setHeader("foo", "bar") - .build()); -messages.add(MessageBuilder.withPayload("\"Bubbles\"".getBytes()).setHeader("foo", "bar") - .build()); - -Flux> clientResponseObserver = - GrpcUtils.biStreaming("localhost", FunctionGrpcProperties.GRPC_PORT, Flux.fromIterable(messages)); - -List> results = clientResponseObserver.collectList().block(Duration.ofSeconds(1)); -``` - -You can see that gRPC stream is mapped to instance of `Flux` from [project reactor](https://projectreactor.io/) - -Unlike the _request/reply_ and _server-side streaming_, you can ONLY pass function definition via property or environment variable. - -#### Pluggable protobuf extension - -While the core data object and its corresponding schema <> are modeled after Spring Message and can represent -virtually any object, there are times when you may want to plug-in your own protobuf services. - -Spring Cloud Function provides such support by allowing you to develop extensions, which once exist could be enabled by simply -including its dependency in the POM. Such extensions are just another spring-boot project that has dependency on `spring-cloud-function-grpc` - -```xml - - org.springframework.cloud - spring-cloud-function-grpc - -``` - -It must also contain 3 classes; 1) Its configuration class, 2) Type converter for the actual protobuf 'message'and 3) Service handler -where you would normally implement your handling functionality. However instead of implementing full functionality you can model your service -after MessagingService provided by us and if you do you can rely on the existing implementation of the core interaction models provided by gRPC - -In fact Spring Cloud Function provides one of such extensions to support [Cloud Events](https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext) proto, so you can model yours after it. - -#### Multiple services on classpath - -With the protobuf extension mentioned in the previous section you may very well end up with several services on the classpath. -By default each available service will be enabled. However, if your intention is to only use one, you can specify which one by providing -its class name via `spring.cloud.function.grpc.service-class-name` property: - -``` ---spring.cloud.function.grpc.service-class-name=org.springframework.cloud.function.grpc.ce.CloudEventHandler -``` \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-grpc/pom.xml deleted file mode 100644 index ab216bea5..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/pom.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - 4.0.0 - spring-cloud-function-grpc - jar - spring-cloud-function-grpc - Spring Cloud Function gRPC Support - - org.springframework.cloud - spring-cloud-function-adapter-parent - 4.1.0-SNAPSHOT - - - 1.55.1 - true - - - - javax.annotation - javax.annotation-api - 1.3.2 - - - io.grpc - grpc-netty - ${grpc.version} - - - io.grpc - grpc-protobuf - ${grpc.version} - - - io.grpc - grpc-services - ${grpc.version} - - - io.grpc - grpc-stub - ${grpc.version} - - - org.springframework.cloud - spring-cloud-function-context - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.boot - spring-boot-starter-test - test - - - org.awaitility - awaitility - test - - - - - - kr.motd.maven - os-maven-plugin - 1.6.1 - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - - org.xolstice.maven.plugins - protobuf-maven-plugin - 0.6.1 - - - com.google.protobuf:protoc:3.23.0:exe:${os.detected.classifier} - - grpc-java - - io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} - - - - - - compile - compile-custom - - - - - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/AbstractGrpcMessageConverter.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/AbstractGrpcMessageConverter.java deleted file mode 100644 index 7a8a76fda..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/AbstractGrpcMessageConverter.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc; - -import org.springframework.messaging.Message; - -import com.google.protobuf.GeneratedMessageV3; - -/** - * - * @author Oleg Zhurakousky - * - * @param instance of {@link GeneratedMessageV3} - */ -public abstract class AbstractGrpcMessageConverter implements GrpcMessageConverter { - - @Override - public Message toSpringMessage(T grpcMessage) { - if (this.supports(grpcMessage)) { - return this.doToSpringMessage(grpcMessage); - } - return null; - } - - @Override - public T fromSpringMessage(Message springMessage, Class grpcClass) { - if (this.supports(grpcClass)) { - return this.doFromSpringMessage(springMessage); - } - return null; - } - - protected abstract Message doToSpringMessage(T grpcMessage); - - - protected abstract T doFromSpringMessage(Message springMessage); - - protected boolean supports(T grpcMessage) { -// String fieldName = grpcMessage.getAllFields().keySet().iterator().next().getFullName(); -// fieldName = fieldName.substring(0, fieldName.lastIndexOf(".")); -// System.out.println(grpcMessage.getClass().getName()); -// return fieldName.contains(grpcMessage.getClass().getSimpleName()); - return this.supports(grpcMessage.getClass()); - } - - protected abstract boolean supports(Class grpcClass); -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/FunctionGrpcProperties.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/FunctionGrpcProperties.java deleted file mode 100644 index dd1427688..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/FunctionGrpcProperties.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.function.context.FunctionProperties; - -/** - * - * @author Oleg Zhurakousky - * @since 3.2 - * - */ -@ConfigurationProperties(prefix = FunctionProperties.PREFIX + ".grpc") -public class FunctionGrpcProperties { - - private final static String GRPC_PREFIX = FunctionProperties.PREFIX + ".grpc"; - /** - * The name of function definition property. - */ - public final static String SERVICE_CLASS_NAME = GRPC_PREFIX + ".service-class-name"; - - /** - * Default gRPC port. - */ - public final static int GRPC_PORT = 6048; - - /** - * gRPC port server will bind to. Default 6048; - */ - private int port = GRPC_PORT; - - /** - * The fully qualified name of the service you wish to enable/expose. - * Setting this property ensures that only a single service is enabled/exposed, - * regardless of how many services are available on the classpath. - */ - private String serviceClassName; - - /** - * Grpc Server port. - */ - public int getPort() { - return this.port; - } - - public void setPort(int port) { - this.port = port; - } - - - public String getServiceClassName() { - return serviceClassName; - } - - - public void setServiceClassName(String serviceClassName) { - this.serviceClassName = serviceClassName; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcAutoConfiguration.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcAutoConfiguration.java deleted file mode 100644 index 005cbaf7f..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcAutoConfiguration.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc; - -import java.util.List; - -import io.grpc.BindableService; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * - * @author Oleg Zhurakousky - * @since 3.2 - */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(FunctionGrpcProperties.class) -@ConditionalOnProperty(name = "spring.cloud.function.grpc.server", havingValue = "true", matchIfMissing = true) -public class GrpcAutoConfiguration { - - @Bean - public GrpcServer grpcServer(FunctionGrpcProperties grpcProperties, BindableService[] grpcMessagingServices) { - Assert.notEmpty(grpcMessagingServices, "'grpcMessagingServices' must not be null or empty"); - if (StringUtils.hasText(grpcProperties.getServiceClassName())) { - for (BindableService bindableService : grpcMessagingServices) { - if (bindableService.getClass().getName().equals(grpcProperties.getServiceClassName())) { - return new GrpcServer(grpcProperties, new BindableService[] {bindableService}); - } - } - } - return new GrpcServer(grpcProperties, grpcMessagingServices); - } - - - @SuppressWarnings("rawtypes") - @Bean - @ConditionalOnMissingBean - public BindableService grpcSpringMessageHandler(MessageHandlingHelper helper) { - return new GrpcServerMessageHandler(helper); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Bean - public MessageHandlingHelper grpcMessageHandlingHelper(List> grpcConverters, - FunctionProperties funcProperties, FunctionCatalog functionCatalog) { - return new MessageHandlingHelper(grpcConverters, functionCatalog, funcProperties); - } - - @Bean - public GrpcSpringMessageConverter grpcSpringMessageConverter() { - return new GrpcSpringMessageConverter(); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcClientTemplate.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcClientTemplate.java deleted file mode 100644 index 7699218d2..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcClientTemplate.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.springframework.cloud.function.grpc; - -public class GrpcClientTemplate { - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcMessageConverter.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcMessageConverter.java deleted file mode 100644 index d9ae88d75..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcMessageConverter.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc; - -import com.google.protobuf.GeneratedMessageV3; - -import org.springframework.messaging.Message; - -/** - * - * @author Oleg Zhurakousky - * - * @param instance of {@link GeneratedMessageV3} - */ -public interface GrpcMessageConverter { - - Message toSpringMessage(T grpcMessage); - - T fromSpringMessage(Message springMessage, Class grpcClass); -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcServer.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcServer.java deleted file mode 100644 index feecafe38..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcServer.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc; - -import java.util.Collections; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import io.grpc.BindableService; -import io.grpc.Server; -import io.grpc.ServerBuilder; -import io.grpc.protobuf.services.ProtoReflectionService; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.context.EnvironmentAware; -import org.springframework.context.SmartLifecycle; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.Environment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.util.ClassUtils; - -/** - * - * @author Oleg Zhurakousky - * @author Dave Syer - * @author Chris Bono - * - * @since 3.2 - * - */ -class GrpcServer implements SmartLifecycle, EnvironmentAware { - - private Log logger = LogFactory.getLog(GrpcServer.class); - - private final FunctionGrpcProperties grpcProperties; - - private final BindableService[] grpcMessageServices; - - private final ExecutorService executor = Executors.newSingleThreadExecutor(); - - private Server server; - - private Environment environment; - - GrpcServer(FunctionGrpcProperties grpcProperties, BindableService[] grpcMessageServices) { - this.grpcProperties = grpcProperties; - this.grpcMessageServices = grpcMessageServices; - } - - @Override - public void start() { - this.executor.execute(() -> { - try { - ServerBuilder serverBuilder = ServerBuilder.forPort(this.grpcProperties.getPort()); - for (int i = 0; i < this.grpcMessageServices.length; i++) { - BindableService bindableService = this.grpcMessageServices[i]; - serverBuilder.addService(bindableService); - } - if (ClassUtils.isPresent("io.grpc.protobuf.services.ProtoReflectionService", null)) { - serverBuilder.addService(ProtoReflectionService.newInstance()); - } - this.server = serverBuilder.build(); - - logger.info("Starting gRPC server"); - this.server.start(); - logger.info("gRPC server is listening on port " + this.server.getPort()); - - if (environment instanceof ConfigurableEnvironment) { - ((ConfigurableEnvironment) this.environment).getPropertySources().addFirst( - new MapPropertySource("grpcServerProps", Collections.singletonMap("local.grpc.server.port", server.getPort()))); - } - } - catch (Exception e) { - stop(); - throw new IllegalStateException(e); - } - }); - } - - @Override - public void stop() { - logger.info("Shutting down gRPC server"); - this.server.shutdownNow(); - this.executor.shutdownNow(); - } - - @Override - public boolean isRunning() { - return this.server != null && !this.server.isShutdown(); - } - - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcServerMessageHandler.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcServerMessageHandler.java deleted file mode 100644 index f04405846..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcServerMessageHandler.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc; - -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -// -import io.grpc.Status; -import io.grpc.stub.ServerCallStreamObserver; -import io.grpc.stub.StreamObserver; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Sinks; -import reactor.core.publisher.Sinks.Many; -// -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.grpc.MessagingServiceGrpc.MessagingServiceImplBase; -import org.springframework.context.SmartLifecycle; -import org.springframework.messaging.Message; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; - -import com.google.protobuf.GeneratedMessageV3; -// -//import com.google.protobuf.GeneratedMessage; - - -/** - * - * @author Oleg Zhurakousky - * @since 3.2 - * - */ -@SuppressWarnings("rawtypes") -public class GrpcServerMessageHandler extends MessagingServiceImplBase { - - private Log logger = LogFactory.getLog(GrpcServerMessageHandler.class); - - private final MessageHandlingHelper helper; - - private boolean running; - - public GrpcServerMessageHandler(MessageHandlingHelper helper) { - this.helper = helper; - } - - @Override - @SuppressWarnings("unchecked") - public void requestReply(GrpcSpringMessage request, StreamObserver responseObserver) { - this.helper.requestReply(request, responseObserver); - } - - @Override - @SuppressWarnings("unchecked") - public void serverStream(GrpcSpringMessage request, StreamObserver responseObserver) { - this.helper.serverStream(request, responseObserver); - } - - @Override - @SuppressWarnings("unchecked") - public StreamObserver clientStream(StreamObserver responseObserver) { - return this.helper.clientStream(responseObserver, GrpcSpringMessage.class); - } - - @SuppressWarnings("unchecked") - @Override - public StreamObserver biStream(StreamObserver responseObserver) { - return this.helper.biStream(responseObserver, GrpcSpringMessage.class); - } -} - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcSpringMessageConverter.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcSpringMessageConverter.java deleted file mode 100644 index 249cbb80b..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcSpringMessageConverter.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.springframework.cloud.function.grpc; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -import com.google.protobuf.ByteString; -import com.google.protobuf.GeneratedMessageV3; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class GrpcSpringMessageConverter extends AbstractGrpcMessageConverter { - - @Override - protected Message doToSpringMessage(GrpcSpringMessage grpcMessage) { - return MessageBuilder.withPayload(grpcMessage.getPayload().toByteArray()) - .copyHeaders(grpcMessage.getHeadersMap()) - .build(); - } - - @Override - protected GrpcSpringMessage doFromSpringMessage(Message springMessage) { - Map stringHeaders = new HashMap<>(); - springMessage.getHeaders().forEach((k, v) -> { - stringHeaders.put(k, v.toString()); - }); - return GrpcSpringMessage.newBuilder() - .setPayload(ByteString.copyFrom(springMessage.getPayload())) - .putAllHeaders(stringHeaders) - .build(); - } - - @Override - protected boolean supports(Class grpcClass) { - return grpcClass.isAssignableFrom(GrpcSpringMessage.class); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcUtils.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcUtils.java deleted file mode 100644 index 7ed7d7117..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/GrpcUtils.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - - -import com.google.protobuf.ByteString; -import io.grpc.ManagedChannel; -import io.grpc.ManagedChannelBuilder; -import io.grpc.Status; -import io.grpc.stub.ClientCallStreamObserver; -import io.grpc.stub.ClientResponseObserver; -import io.grpc.stub.StreamObserver; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Sinks; -import reactor.core.publisher.Sinks.Many; - -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -/** - * - * @author Oleg Zhurakousky - * @since 3.2 - * - */ -public final class GrpcUtils { - - private static Log logger = LogFactory.getLog(GrpcUtils.class); - - private GrpcUtils() { - - } - - public static GrpcSpringMessage toGrpcSpringMessage(byte[] payload, Map headers) { - return GrpcSpringMessage.newBuilder() - .setPayload(ByteString.copyFrom(payload)) - .putAllHeaders(headers) - .build(); - } - - public static GrpcSpringMessage toGrpcSpringMessage(Message message) { - Map stringHeaders = new HashMap<>(); - message.getHeaders().forEach((k, v) -> { - stringHeaders.put(k, v.toString()); - }); - return toGrpcSpringMessage(message.getPayload(), stringHeaders); - } - - public static Message fromGrpcSpringMessage(GrpcSpringMessage message) { - return MessageBuilder.withPayload(message.getPayload().toByteArray()) - .copyHeaders(message.getHeadersMap()) - .build(); - } - - public static Message requestReply(Message inputMessage) { - return requestReply("localhost", FunctionGrpcProperties.GRPC_PORT, inputMessage); - } - - public static Message requestReply(String host, int port, Message inputMessage) { - ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port) - .usePlaintext().build(); - try { - MessagingServiceGrpc.MessagingServiceBlockingStub stub = MessagingServiceGrpc - .newBlockingStub(channel); - - try { - GrpcSpringMessage response = stub.requestReply(toGrpcSpringMessage(inputMessage)); - return fromGrpcSpringMessage(response); - } - catch (Exception e) { - throw new IllegalStateException(e); - } - } - finally { - channel.shutdownNow(); - } - } - - /** - * Utility method to support bi-directional streaming interaction. Will connect to gRPC server using default host/port, - * otherwise use {@link #biStreaming(String, int, Flux)} method. - * - * Keep in mind that there is no implied relationship between input stream and output stream. - * They are completely independent where one may end before the other. - * - * @param inputStream {@code FluxMessage>} representing input stream. - * @return {@code Flux>} representing output stream - */ - public static Flux> biStreaming(Flux> inputStream) { - return biStreaming("localhost", FunctionGrpcProperties.GRPC_PORT, inputStream); - } - - /** - * Utility method to support bi-directional streaming interaction. - * Keep in mind that there is no implied relationship between input stream and output stream. - * They are completely independent where one may end before the other. - * - * @param host gRPC server host name - * @param port gRPC server port - * @param inputStream {@code FluxMessage>} representing input stream - * @return {@code Flux>} representing output stream - */ - public static Flux> biStreaming(String host, int port, Flux> inputStream) { - ManagedChannel channel = ManagedChannelBuilder - .forAddress(host, port) - .usePlaintext().build(); - MessagingServiceGrpc.MessagingServiceStub stub = MessagingServiceGrpc - .newStub(channel); - Many> sink = Sinks.many().unicast().onBackpressureBuffer(); - - ClientResponseObserver clientResponseObserver = clientResponseObserver(inputStream, sink); - - stub.biStream(clientResponseObserver); - - return sink.asFlux().doOnComplete(() -> { - logger.debug("Shutting down channel"); - channel.shutdownNow(); - }) - .doOnError(e -> { - e.printStackTrace(); - channel.shutdownNow(); - }); - } - - public static Flux> serverStream(String host, int port, Message inputMessage) { - ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port) - .usePlaintext().build(); - MessagingServiceGrpc.MessagingServiceBlockingStub stub = MessagingServiceGrpc - .newBlockingStub(channel); - - Iterator serverStream = stub.serverStream(toGrpcSpringMessage(inputMessage)); - - Many> sink = Sinks.many().unicast().onBackpressureBuffer(); - ExecutorService executor = Executors.newSingleThreadExecutor(); - executor.execute(() -> { - while (serverStream.hasNext()) { - GrpcSpringMessage grpcMessage = serverStream.next(); - sink.tryEmitNext(GrpcUtils.fromGrpcSpringMessage(grpcMessage)); - } - sink.tryEmitComplete(); - }); - - return sink.asFlux() - .doOnComplete(() -> { - channel.shutdownNow(); - executor.shutdownNow(); - }) - .doOnError(e -> { - e.printStackTrace(); - channel.shutdownNow(); - executor.shutdownNow(); - }); - } - - - /** - * Utility method to support client-side streaming interaction. Will connect to gRPC server using default host/port, - * otherwise use {@link #clientStream(String, int, Flux)} method. - * - * @param inputStream {@code FluxMessage>} representing input stream. - * @return {@code Message} representing output - */ - public static Message clientStream(Flux> inputStream) { - return clientStream("localhost", FunctionGrpcProperties.GRPC_PORT, inputStream); - } - - /** - * Utility method to support client-side streaming interaction. - * - * @param host gRPC server host name - * @param port gRPC server port - * @param inputStream {@code FluxMessage>} representing input stream - * @return {@code Message} representing output - */ - public static Message clientStream(String host, int port, Flux> inputStream) { - ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port) - .usePlaintext().build(); - - LinkedBlockingQueue> resultRef = new LinkedBlockingQueue<>(1); - StreamObserver responseObserver = new StreamObserver() { - @Override - public void onNext(GrpcSpringMessage result) { - if (logger.isDebugEnabled()) { - logger.debug("Client received reply: " + result); - } - resultRef.offer(GrpcUtils.fromGrpcSpringMessage(result)); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - channel.shutdownNow(); - } - - @Override - public void onCompleted() { - logger.info("Client completed"); - channel.shutdownNow(); - } - }; - - MessagingServiceGrpc.MessagingServiceStub asyncStub = MessagingServiceGrpc.newStub(channel); - - StreamObserver requestObserver = asyncStub.clientStream(responseObserver); - - inputStream.doOnNext(message -> { - if (logger.isDebugEnabled()) { - logger.debug("Client sending: " + message); - } - try { - requestObserver.onNext(GrpcUtils.toGrpcSpringMessage(message)); - } - catch (Exception e) { - requestObserver.onError(e); - } - }).doOnComplete(() -> { - requestObserver.onCompleted(); - }).doOnError(e -> { - e.printStackTrace(); - responseObserver.onError(Status.UNKNOWN.withDescription("Error handling request") - .withCause(e).asRuntimeException()); - }) - .subscribe(); - - try { - return resultRef.poll(Integer.MAX_VALUE, TimeUnit.MILLISECONDS); - } - catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - throw new IllegalStateException(ie); - } - } - - private static ClientResponseObserver clientResponseObserver(Flux> inputStream, Many> sink) { - return new ClientResponseObserver() { - - ClientCallStreamObserver requestStreamObserver; - - @Override - public void beforeStart(ClientCallStreamObserver requestStreamObserver) { - this.requestStreamObserver = requestStreamObserver; - requestStreamObserver.disableAutoInboundFlowControl(); - - requestStreamObserver.setOnReadyHandler(new Runnable() { - @Override - public void run() { - inputStream - .doOnNext(request -> { - if (logger.isDebugEnabled()) { - logger.debug("Streaming message to function: " + request); - } - requestStreamObserver.onNext(GrpcUtils.toGrpcSpringMessage(request)); - }) - .doOnComplete(() -> { - requestStreamObserver.onCompleted(); - }) - .subscribe(); - } - }); - } - - @Override - public void onNext(GrpcSpringMessage message) { - if (logger.isDebugEnabled()) { - logger.debug("Streaming message from function: " + message); - } - sink.tryEmitNext(fromGrpcSpringMessage(message)); - requestStreamObserver.request(1); - - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - } - - @Override - public void onCompleted() { - logger.info("Client stream is complete"); - sink.tryEmitComplete(); // TODO revisit as this would complete the server stream simply because the client is done. - // Perhaps we need to expose some boolean value when this is desirable - } - }; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/MessageHandlingHelper.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/MessageHandlingHelper.java deleted file mode 100644 index c24e3cd4f..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/java/org/springframework/cloud/function/grpc/MessageHandlingHelper.java +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import com.google.protobuf.ByteString; -import com.google.protobuf.GeneratedMessageV3; - -import io.grpc.Status; -import io.grpc.stub.ServerCallStreamObserver; -import io.grpc.stub.StreamObserver; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.core.publisher.Sinks; -import reactor.core.publisher.Sinks.Many; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.context.SmartLifecycle; -import org.springframework.messaging.Message; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; - -/** - * - * @author Oleg Zhurakousky - * @since 3.2 - */ -public class MessageHandlingHelper implements SmartLifecycle { - - private Log logger = LogFactory.getLog(MessageHandlingHelper.class); - - private final List> grpcConverters; - - private final FunctionProperties funcProperties; - - private final FunctionCatalog functionCatalog; - - private final ExecutorService executor; - - private boolean running; - - public MessageHandlingHelper(List> grpcConverters, - FunctionCatalog functionCatalog, FunctionProperties funcProperties) { - this.grpcConverters = grpcConverters; - this.funcProperties = funcProperties; - this.functionCatalog = functionCatalog; - this.executor = Executors.newCachedThreadPool(); - } - - @SuppressWarnings("unchecked") - public void requestReply(T request, StreamObserver responseObserver) { - Message message = this.toSpringMessage(request); - FunctionInvocationWrapper function = this.resolveFunction(message.getHeaders()); - if (FunctionTypeUtils.isFlux(function.getOutputType())) { - String errorMessage = "Flux reply is not supported for `requestReply` mode"; - responseObserver.onError(Status.UNKNOWN.withDescription(errorMessage) - .withCause(new UnsupportedOperationException(errorMessage)).asRuntimeException()); - return; - } - - Object replyMessage = function.apply(message); - if (replyMessage instanceof Message) { - GeneratedMessageV3 reply = this.toGrpcMessage((Message) replyMessage, (Class) request.getClass()); - responseObserver.onNext((T) reply); - responseObserver.onCompleted(); - } - else if (replyMessage instanceof Publisher) { - if (replyMessage instanceof Mono) { - Mono.from((Publisher) replyMessage).doOnNext(reply -> { - GeneratedMessageV3 replyGrps = this.toGrpcMessage((Message) reply, (Class) request.getClass()); - responseObserver.onNext((T) replyGrps); - responseObserver.onCompleted(); - }) - .subscribe(); - } - } - } - - @SuppressWarnings("unchecked") - public void serverStream(T request, StreamObserver responseObserver) { - Message message = this.toSpringMessage(request); - FunctionInvocationWrapper function = this.resolveFunction(message.getHeaders()); - Publisher> replyStream = (Publisher>) function.apply(message); - Flux.from(replyStream).doOnNext(replyMessage -> { - responseObserver.onNext(this.toGrpcMessage(replyMessage, (Class) request.getClass())); - }) - .doOnComplete(() -> responseObserver.onCompleted()) - .subscribe(); - } - - @SuppressWarnings("unchecked") - public StreamObserver clientStream(StreamObserver responseObserver, Class grpcMessageType) { - ServerCallStreamObserver serverCallStreamObserver = (ServerCallStreamObserver) responseObserver; - serverCallStreamObserver.disableAutoInboundFlowControl(); - - FunctionInvocationWrapper function = this.resolveFunction(null); - - AtomicBoolean wasReady = new AtomicBoolean(false); - serverCallStreamObserver.setOnReadyHandler(() -> { - if (serverCallStreamObserver.isReady() && !wasReady.get()) { - wasReady.set(true); - logger.info("gRPC Server receiving stream is ready."); - serverCallStreamObserver.request(1); - } - }); - - if (!function.isInputTypePublisher()) { - throw new UnsupportedOperationException("The client streaming is " - + "not supported for functions that accept non-Publisher: " - + function); - } - else if (function.isOutputTypePublisher()) { - throw new UnsupportedOperationException("The client streaming is " - + "not supported for functions that return Publisher: " - + function); - } - else { - Many> inputStream = Sinks.many().unicast().onBackpressureBuffer(); - Flux> inputStreamFlux = inputStream.asFlux(); - - LinkedBlockingQueue> resultRef = new LinkedBlockingQueue<>(1); - this.executor.execute(() -> { - Message replyMessage = (Message) function.apply(inputStreamFlux); - if (logger.isDebugEnabled()) { - logger.debug("Function invocation reply: " + replyMessage); - } - resultRef.offer(replyMessage); - }); - - return new StreamObserver() { - @Override - public void onNext(T inputMessage) { - if (logger.isDebugEnabled()) { - logger.debug("gRPC Server receiving: " + inputMessage); - } - inputStream.tryEmitNext(toSpringMessage(inputMessage)); - serverCallStreamObserver.request(1); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - responseObserver.onError(Status.UNKNOWN.withDescription("Error handling request") - .withCause(t).asRuntimeException()); - } - - @Override - public void onCompleted() { - logger.info("gRPC Server has finished receiving data."); - inputStream.tryEmitComplete(); - try { - responseObserver.onNext(toGrpcMessage(resultRef.poll(Integer.MAX_VALUE, TimeUnit.MILLISECONDS), grpcMessageType)); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - finally { - responseObserver.onCompleted(); - } - } - }; - } - } - - public StreamObserver biStream(StreamObserver responseObserver, Class grpcMessageType) { - ServerCallStreamObserver serverCallStreamObserver = (ServerCallStreamObserver) responseObserver; - serverCallStreamObserver.disableAutoInboundFlowControl(); - - FunctionInvocationWrapper function = this.resolveFunction(null); - - AtomicBoolean wasReady = new AtomicBoolean(false); - serverCallStreamObserver.setOnReadyHandler(() -> { - if (serverCallStreamObserver.isReady() && !wasReady.get()) { - wasReady.set(true); - logger.info("gRPC Server receiving stream is ready."); - serverCallStreamObserver.request(1); - } - }); - - if (function.isInputTypePublisher()) { - if (function.isOutputTypePublisher()) { - return this.biStreamReactive(responseObserver, serverCallStreamObserver, grpcMessageType); - } - UnsupportedOperationException ex = new UnsupportedOperationException("The bi-directional streaming is " - + "not supported for functions that accept Publisher but return non-Publisher: " - + function); - responseObserver.onCompleted(); - throw ex; - } - else { - if (!function.isOutputTypePublisher()) { - return this.biStreamImperative(responseObserver, serverCallStreamObserver, wasReady); - } - - UnsupportedOperationException ex = new UnsupportedOperationException("The bidirection streaming is " - + "not supported for functions that accept non-Publisher but return Publisher: " - + function); - responseObserver.onCompleted(); - throw ex; - } - } - - @SuppressWarnings("unchecked") - private StreamObserver biStreamReactive(StreamObserver responseObserver, - ServerCallStreamObserver serverCallStreamObserver, Class grpcMessageType) { - Many> inputStream = Sinks.many().unicast().onBackpressureBuffer(); - Flux> inputStreamFlux = inputStream.asFlux(); - - FunctionInvocationWrapper function = this.resolveFunction(null); - - Publisher> outputPublisher = (Publisher>) function.apply(inputStreamFlux); - - Flux.from(outputPublisher).subscribe(functionResult -> { - T outputMessage = toGrpcMessage(functionResult, grpcMessageType); - if (logger.isDebugEnabled()) { - logger.debug("gRPC Server replying: " + outputMessage); - } - responseObserver.onNext(outputMessage); - }); - - return new StreamObserver() { - @Override - public void onNext(T inputMessage) { - if (logger.isDebugEnabled()) { - logger.debug("gRPC Server receiving: " + inputMessage); - } - inputStream.tryEmitNext(toSpringMessage(inputMessage)); - serverCallStreamObserver.request(1); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - inputStream.tryEmitComplete(); - responseObserver.onError(Status.UNKNOWN.withDescription("Error handling request") - .withCause(t).asException()); - } - - @Override - public void onCompleted() { - logger.info("gRPC Server has finished receiving data."); - inputStream.tryEmitComplete(); - responseObserver.onCompleted(); - } - }; - } - - private StreamObserver biStreamImperative(StreamObserver responseObserver, - ServerCallStreamObserver serverCallStreamObserver, - AtomicBoolean wasReady) { - return new StreamObserver() { - - @SuppressWarnings("unchecked") - @Override - public void onNext(T request) { - try { - Message message = toSpringMessage(request); - FunctionInvocationWrapper function = resolveFunction( - message.getHeaders()); - - Message replyMessage = (Message) function - .apply(message); - - T reply = toGrpcMessage(replyMessage, (Class) request.getClass()); - - responseObserver.onNext(reply); - - // Check the provided ServerCallStreamObserver to see if it is still - // ready to accept more messages. - if (serverCallStreamObserver.isReady()) { - serverCallStreamObserver.request(1); - } - else { - wasReady.set(false); - } - } - catch (Throwable throwable) { - throwable.printStackTrace(); - responseObserver.onError( - Status.UNKNOWN.withDescription("Error handling request") - .withCause(throwable).asException()); - } - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - responseObserver.onCompleted(); - } - - @Override - public void onCompleted() { - logger.info("gRPC Server has finished receiving data."); - responseObserver.onCompleted(); - } - }; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private T toGrpcMessage(Message request, Class grpcClass) { - for (GrpcMessageConverter converter : this.grpcConverters) { - GeneratedMessageV3 grpcMessage = converter.fromSpringMessage(request, grpcClass); - if (grpcMessage != null) { - return (T) grpcMessage; - } - } - throw new IllegalStateException("Failed to convert Grpc Message to Spring Message: " + request); - } - - @Override - public void start() { - this.running = true; - } - - @Override - public void stop() { - this.executor.shutdown(); - try { - Assert.isTrue(this.executor.awaitTermination(5000, TimeUnit.MILLISECONDS), "gRPC Server executor timed out while stopping, " - + "since there are currently executing tasks"); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - this.running = false; - } - - @Override - public boolean isRunning() { - return this.running; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private Message toSpringMessage(GeneratedMessageV3 request) { - for (GrpcMessageConverter converter : this.grpcConverters) { - Message springMessage = converter.toSpringMessage(request); - if (springMessage != null) { - return springMessage; - } - } - throw new IllegalStateException("Failed to convert Grpc Message to Spring Message: " + request); - } - - private FunctionInvocationWrapper resolveFunction(Map headers) { - String functionDefinition = funcProperties.getDefinition(); - if (!CollectionUtils.isEmpty(headers) && headers.containsKey(FunctionProperties.FUNCTION_DEFINITION)) { - functionDefinition = (String) headers.get(FunctionProperties.FUNCTION_DEFINITION); - } - FunctionInvocationWrapper function = this.functionCatalog.lookup(functionDefinition, "application/json"); - Assert.notNull(function, () -> "Failed to lookup function " + funcProperties.getDefinition()); - return function; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/proto/MessageService.proto b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/proto/MessageService.proto deleted file mode 100644 index 602be654d..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/proto/MessageService.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; -option java_multiple_files = true; -package org.springframework.cloud.function.grpc; - -message GrpcSpringMessage { - bytes payload = 1; - map headers = 2; -} - -service MessagingService { - rpc biStream(stream GrpcSpringMessage) returns (stream GrpcSpringMessage); - - rpc clientStream(stream GrpcSpringMessage) returns (GrpcSpringMessage); - - rpc serverStream(GrpcSpringMessage) returns (stream GrpcSpringMessage); - - rpc requestReply(GrpcSpringMessage) returns (GrpcSpringMessage); -} \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 1aee89ea4..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -org.springframework.cloud.function.grpc.GrpcAutoConfiguration diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java deleted file mode 100644 index ef0d66c7b..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.grpc; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import java.util.function.Function; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.utils.SocketUtils; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeTypeUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * - * @author Oleg Zhurakousky - * @author Chris Bono - */ -@Disabled -public class GrpcInteractionTests { - - @BeforeEach - public void before() { - System.clearProperty("spring.cloud.function.definition"); - } - - @AfterEach - public void after() { - System.clearProperty("spring.cloud.function.definition"); - } - - @Test - public void testRequestReply() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.definition=uppercase", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes()) - .setHeader("foo", "bar") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN) - .build(); - - Message reply = GrpcUtils.requestReply("localhost", port, message); - - assertThat(reply.getPayload()).isEqualTo("\"HELLO GRPC\"".getBytes()); - } - } - - @Test - public void testRequestReplyWithMonoReturn() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.definition=uppercaseMonoReturn", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes()) - .setHeader("foo", "bar") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN) - .build(); - - Message reply = GrpcUtils.requestReply("localhost", port, message); - - assertThat(reply.getPayload()).isEqualTo("\"HELLO GRPC\"".getBytes()); - } - } - - @Test - public void testRequestReplyWithFluxReturn() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.definition=uppercaseFluxReturn", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes()) - .setHeader("foo", "bar") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN) - .build(); - try { - GrpcUtils.requestReply("localhost", port, message); - fail(); - } - catch (Exception e) { - assertThat(e.getMessage()).contains("Flux reply is not supported for `requestReply` mode"); - } - } - } - - @Test - public void testRequstReplyFunctionDefinitionInMessage() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes()) - .setHeader("foo", "bar") - .setHeader("spring.cloud.function.definition", "reverse") - .build(); - - Message reply = GrpcUtils.requestReply("localhost", port, message); - - assertThat(reply.getPayload()).isEqualTo("\"CPRg olleh\"".getBytes()); - } - } - - @Test - public void testBidirectionalStreamWithImperativeFunction() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.definition=uppercase", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - List> messages = new ArrayList<>(); - messages.add(MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar") - .build()); - messages.add(MessageBuilder.withPayload("\"Julien\"".getBytes()).setHeader("foo", "bar") - .build()); - messages.add(MessageBuilder.withPayload("\"Bubbles\"".getBytes()).setHeader("foo", "bar") - .build()); - - Flux> clientResponseObserver = - GrpcUtils.biStreaming("localhost", port, Flux.fromIterable(messages)); - - List> results = clientResponseObserver.collectList().block(Duration.ofSeconds(10)); - assertThat(results.size()).isEqualTo(3); - assertThat(results.get(0).getPayload()).isEqualTo("\"RICKY\"".getBytes()); - assertThat(results.get(1).getPayload()).isEqualTo("\"JULIEN\"".getBytes()); - assertThat(results.get(2).getPayload()).isEqualTo("\"BUBBLES\"".getBytes()); - } - } - - @Test - public void testBidirectionalStreamWithReactiveFunction() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.definition=uppercaseReactive", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - List> messages = new ArrayList<>(); - messages.add(MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN) - .build()); - messages.add(MessageBuilder.withPayload("\"Julien\"".getBytes()).setHeader("foo", "bar") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN) - .build()); - messages.add(MessageBuilder.withPayload("\"Bubbles\"".getBytes()).setHeader("foo", "bar") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN) - .build()); - - Flux> resultStream = - GrpcUtils.biStreaming("localhost", port, Flux.fromIterable(messages)); - - List> results = resultStream.collectList().block(Duration.ofSeconds(5)); - assertThat(results.size()).isEqualTo(3); - assertThat(results.get(0).getPayload()).isEqualTo("\"RICKY\"".getBytes()); - assertThat(results.get(1).getPayload()).isEqualTo("\"JULIEN\"".getBytes()); - assertThat(results.get(2).getPayload()).isEqualTo("\"BUBBLES\"".getBytes()); - } - } - - @Test - public void testClientStreaming() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.definition=streamInStringOut", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - List> messages = new ArrayList<>(); - messages.add(MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar") - .build()); - messages.add(MessageBuilder.withPayload("\"Julien\"".getBytes()).setHeader("foo", "bar") - .build()); - messages.add(MessageBuilder.withPayload("\"Bubbles\"".getBytes()).setHeader("foo", "bar") - .build()); - - Message reply = - GrpcUtils.clientStream("localhost", port, Flux.fromIterable(messages)); - - assertThat(reply.getPayload()).isEqualTo("[Ricky, Julien, Bubbles]".getBytes()); - } - } - - @Test - public void testServerStreaming() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.definition=stringInStreamOut", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - Message message = MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar").build(); - - Flux> reply = - GrpcUtils.serverStream("localhost", port, message); - - List> results = reply.collectList().block(Duration.ofSeconds(10)); - assertThat(results.size()).isEqualTo(2); - assertThat(results.get(0).getPayload()).isEqualTo("\"Ricky\"".getBytes()); - assertThat(results.get(1).getPayload()).isEqualTo("\"RICKY\"".getBytes()); - } - } - - @Test - public void testBiStreamStreamInStringOutFailure() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.definition=streamInStringOut", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - List> messages = new ArrayList<>(); - messages.add(MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar") - .build()); - messages.add(MessageBuilder.withPayload("\"Julien\"".getBytes()).setHeader("foo", "bar") - .build()); - messages.add(MessageBuilder.withPayload("\"Bubbles\"".getBytes()).setHeader("foo", "bar") - .build()); - - Flux> clientResponseObserver = - GrpcUtils.biStreaming("localhost", port, Flux.fromIterable(messages)); - - assertThat(clientResponseObserver.collectList().block(Duration.ofSeconds(2))).isEmpty(); - } - } - - @Test - public void testBiStreamStringInStreamOutFailure() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleConfiguration.class).web(WebApplicationType.NONE).run( - "--spring.jmx.enabled=false", - "--spring.cloud.function.definition=stringInStreamOut", - "--spring.cloud.function.grpc.port=0")) { - - int port = patientlyGetPort(context); - - List> messages = new ArrayList<>(); - messages.add(MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar") - .build()); - messages.add(MessageBuilder.withPayload("\"Julien\"".getBytes()).setHeader("foo", "bar") - .build()); - messages.add(MessageBuilder.withPayload("\"Bubbles\"".getBytes()).setHeader("foo", "bar") - .build()); - - Flux> clientResponseObserver = - GrpcUtils.biStreaming("localhost", port, Flux.fromIterable(messages)); - - assertThat(clientResponseObserver.collectList().block(Duration.ofSeconds(2))).isEmpty(); - } - } - - private int patientlyGetPort(ConfigurableApplicationContext context) throws InterruptedException { - await() - .pollDelay(Duration.ofMillis(500)) - .pollInterval(Duration.ofMillis(500)) - .atMost(Duration.ofSeconds(3)) - .untilAsserted(() -> { - String port = context.getEnvironment().getProperty("local.grpc.server.port"); - assertThat(port).as("Unable to get 'local.grpc.server.port' - server may not have started up").isNotEmpty(); - }); - return Integer.valueOf(context.getEnvironment().getProperty("local.grpc.server.port")); - } - - @EnableAutoConfiguration - public static class SampleConfiguration { - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - @Bean - public Function> uppercaseMonoReturn() { - return v -> Mono.just(v.toUpperCase()); - } - - @Bean - public Function> uppercaseFluxReturn() { - return v -> Flux.just(v.toUpperCase(), v.toUpperCase() + "-1", v.toUpperCase() + "-2"); - } - - @Bean - public Function reverse() { - return v -> new StringBuilder(v).reverse().toString(); - } - - @Bean - public Function, Flux> uppercaseReactive() { - return flux -> flux.map(v -> v.toUpperCase()); - } - - @Bean - public Function, String> streamInStringOut() { - return flux -> flux.doOnNext(v -> { - try { - Thread.sleep(new Random().nextInt(200)); // artificial delay - } - catch (Exception e) { - // ignore - } - }).collectList().block().toString(); - } - - @Bean - public Function> stringInStreamOut() { - return value -> Flux.just(value, value.toUpperCase()); - } - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/resources/application.properties b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/resources/application.properties deleted file mode 100644 index 1aab44a8b..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -logging.level.org.springframework.cloud.function.grpc=DEBUG diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/.jdk8 b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/README.md b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/README.md deleted file mode 100644 index b5f51b0ec..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/README.md +++ /dev/null @@ -1,14 +0,0 @@ -#### Introduction - -This module represents a concept of a light weight AWS forwarding proxy which deploys and interacts with existing -Spring Boot web application deployed as AWS Lambda. - - -A sample is provided in [sample](https://github.com/spring-cloud/spring-cloud-function/tree/serverless-web/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/sample/pet-store) directory. It contain README and SAM template file to simplify the deployment. This module is identified as the only additional dependnecy to the existing web-app. - -_NOTE: Although this module is AWS specific, this dependency is protocol only (not binary), therefore there is no AWS dependnecies._ - -The aformentioned proxy is identified as AWS Lambda [handler](https://github.com/spring-cloud/spring-cloud-function/blob/serverless-web/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/sample/pet-store/template.yml#L14) - -The main Spring Boot configuration file is identified as [MAIN_CLASS](https://github.com/spring-cloud/spring-cloud-function/blob/serverless-web/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/sample/pet-store/template.yml#L22) - diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml deleted file mode 100644 index 131038ced..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - 4.0.0 - spring-cloud-function-serverless-web - jar - spring-cloud-function-serverless-web - Base serverless web adapter - - org.springframework.cloud - spring-cloud-function-adapter-parent - 4.1.0-SNAPSHOT - - - UTF-8 - UTF-8 - - - - org.springframework - spring-webmvc - - - - - org.springframework.boot - spring-boot-starter-security - provided - - - org.springframework.security - spring-security-test - test - - - - - jakarta.servlet - jakarta.servlet-api - provided - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-web - test - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/.aws-sam/build.toml b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/.aws-sam/build.toml deleted file mode 100644 index 05c08b5fa..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/.aws-sam/build.toml +++ /dev/null @@ -1,13 +0,0 @@ -# This file is auto generated by SAM CLI build command - -[function_build_definitions] -[function_build_definitions.9341c1d5-9265-48ef-836e-25df000b0c59] -codeuri = "/Users/ozhurakousky/Documents/dev/repo/spring-cloud-function/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/sample/pet-store" -runtime = "java11" -architecture = "x86_64" -handler = "org.springframework.cloud.function.adapter.aws.web.WebProxyInvoker::handleRequest" -manifest_hash = "" -packagetype = "Zip" -functions = ["PetStoreFunction"] - -[layer_build_definitions] diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/README.md b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/README.md deleted file mode 100644 index bbe5db289..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/README.md +++ /dev/null @@ -1,38 +0,0 @@ -Copied from https://github.com/awslabs/aws-serverless-java-container/tree/main/samples/spring/pet-store - -# Serverless Spring example -A basic pet store written with the [Spring framework](https://projects.spring.io/spring-framework/). The `StreamLambdaHandler` object is the main entry point for Lambda. - -The application can be deployed in an AWS account using the [Serverless Application Model](https://github.com/awslabs/serverless-application-model). The `template.yml` file in the root folder contains the application definition. - -## Pre-requisites -* [AWS CLI](https://aws.amazon.com/cli/) -* [SAM CLI](https://github.com/awslabs/aws-sam-cli) -* [Gradle](https://gradle.org/) or [Maven](https://maven.apache.org/) - -## Deployment -In a shell, navigate to the sample's folder and use the SAM CLI to build a deployable package -``` -$ sam build -``` - -This command compiles the application and prepares a deployment package in the `.aws-sam` sub-directory. - -To deploy the application in your AWS account, you can use the SAM CLI's guided deployment process and follow the instructions on the screen - -``` -$ sam deploy --guided -``` - -Once the deployment is completed, the SAM CLI will print out the stack's outputs, including the new application URL. You can use `curl` or a web browser to make a call to the URL - -``` -... ---------------------------------------------------------------------------------------------------------- -OutputKey-Description OutputValue ---------------------------------------------------------------------------------------------------------- -PetStoreApi - URL for application https://xxxxxxxxxx.execute-api.us-west-2.amazonaws.com/pets ---------------------------------------------------------------------------------------------------------- - -$ curl https://xxxxxxxxxx.execute-api.us-west-2.amazonaws.com/pets -``` \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/pom.xml deleted file mode 100644 index 84a6d5ea6..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/pom.xml +++ /dev/null @@ -1,192 +0,0 @@ - - - 4.0.0 - - oz.spring.petstore - pet-store - 1.0-SNAPSHOT - pet-store - Simple pet store written with the Spring framework - https://aws.amazon.com/lambda/ - - - https://github.com/awslabs/aws-serverless-java-container.git - - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - 1.8 - 1.8 - 5.3.25 - 4.13.2 - 2.19.0 - - - - - org.springframework.cloud - spring-cloud-function-adapter-aws-web - 3.2.9-SNAPSHOT - - - - org.springframework - spring-context-indexer - ${spring.version} - true - - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - - org.apache.logging.log4j - log4j-api - ${log4j.version} - - - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j.version} - - - - com.amazonaws - aws-lambda-java-log4j2 - 1.5.1 - - - - junit - junit - ${junit.version} - test - - - - - - shaded-jar - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - package - - shade - - - false - - - - - - - - - - com.github.edwgiz - maven-shade-plugin.log4j2-cachefile-transformer - 2.8.1 - - - - - - - - assembly-zip - - true - - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - default-jar - none - - - - - org.apache.maven.plugins - maven-install-plugin - 3.0.0-M1 - - true - - - - - org.apache.maven.plugins - maven-dependency-plugin - 3.2.0 - - - copy-dependencies - package - - copy-dependencies - - - ${project.build.directory}/lib - runtime - - - - - - org.apache.maven.plugins - maven-assembly-plugin - 3.3.0 - - - zip-assembly - package - - single - - - ${project.artifactId}-${project.version} - - src${file.separator}assembly${file.separator}bin.xml - - false - - - - - - - - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/assembly/bin.xml b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/assembly/bin.xml deleted file mode 100644 index 1ffd82d1c..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/assembly/bin.xml +++ /dev/null @@ -1,24 +0,0 @@ - - lambda-package - - zip - - false - - - - ${project.build.directory}${file.separator}lib - lib - - - - ${project.build.directory}${file.separator}classes - - ** - - ${file.separator} - - - \ No newline at end of file diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/PetStoreSpringAppConfig.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/PetStoreSpringAppConfig.java deleted file mode 100644 index 3969ea641..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/PetStoreSpringAppConfig.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package oz.spring.petstore; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.web.servlet.HandlerAdapter; -import org.springframework.web.servlet.HandlerExceptionResolver; -import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - - -@Configuration -@Import({ PetsController.class }) -public class PetStoreSpringAppConfig { - /* - * Create required HandlerMapping, to avoid several default HandlerMapping instances being created - */ - @Bean - public HandlerMapping handlerMapping() { - return new RequestMappingHandlerMapping(); - } - - /* - * Create required HandlerAdapter, to avoid several default HandlerAdapter instances being created - */ - @Bean - public HandlerAdapter handlerAdapter() { - return new RequestMappingHandlerAdapter(); - } - - /* - * optimization - avoids creating default exception resolvers; not required as the serverless container handles - * all exceptions - * - * By default, an ExceptionHandlerExceptionResolver is created which creates many dependent object, including - * an expensive ObjectMapper instance. - * - * To enable custom @ControllerAdvice classes remove this bean. - */ - @Bean - public HandlerExceptionResolver handlerExceptionResolver() { - return new HandlerExceptionResolver() { - - @Override - public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { - return null; - } - }; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/PetsController.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/PetsController.java deleted file mode 100644 index b04a26d92..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/PetsController.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package oz.spring.petstore; - -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; - -import oz.spring.petstore.model.Pet; -import oz.spring.petstore.model.PetData; - -import java.security.Principal; -import java.util.Optional; -import java.util.UUID; - -@RestController -@EnableWebMvc -public class PetsController { - @RequestMapping(path = "/pets", method = RequestMethod.POST) - public Pet createPet(@RequestBody Pet newPet) { - if (newPet.getName() == null || newPet.getBreed() == null) { - return null; - } - - Pet dbPet = newPet; - dbPet.setId(UUID.randomUUID().toString()); - return dbPet; - } - - @RequestMapping(path = "/pets", method = RequestMethod.GET) - public Pet[] listPets(@RequestParam("limit") Optional limit, Principal principal) { - int queryLimit = 10; - if (limit.isPresent()) { - queryLimit = limit.get(); - } - - Pet[] outputPets = new Pet[queryLimit]; - - for (int i = 0; i < queryLimit; i++) { - Pet newPet = new Pet(); - newPet.setId(UUID.randomUUID().toString()); - newPet.setName(PetData.getRandomName()); - newPet.setBreed(PetData.getRandomBreed()); - newPet.setDateOfBirth(PetData.getRandomDoB()); - outputPets[i] = newPet; - } - - return outputPets; - } - - @GetMapping("favicon.ico") - @ResponseBody - void returnNoFavicon() { - } - - @RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET) - public Pet listPets() { - Pet newPet = new Pet(); - newPet.setId(UUID.randomUUID().toString()); - newPet.setBreed(PetData.getRandomBreed()); - newPet.setDateOfBirth(PetData.getRandomDoB()); - newPet.setName(PetData.getRandomName()); - return newPet; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/model/Error.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/model/Error.java deleted file mode 100644 index bb19a9027..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/model/Error.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package oz.spring.petstore.model; - -public class Error { - private String message; - - public Error(String errorMessage) { - message = errorMessage; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/model/Pet.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/model/Pet.java deleted file mode 100644 index 20f170a99..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/model/Pet.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package oz.spring.petstore.model; - -import java.util.Date; - -public class Pet { - private String id; - private String breed; - private String name; - private Date dateOfBirth; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getBreed() { - return breed; - } - - public void setBreed(String breed) { - this.breed = breed; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Date getDateOfBirth() { - return dateOfBirth; - } - - public void setDateOfBirth(Date dateOfBirth) { - this.dateOfBirth = dateOfBirth; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/model/PetData.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/model/PetData.java deleted file mode 100644 index 1df3632cc..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/src/main/java/oz/spring/petstore/model/PetData.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package oz.spring.petstore.model; - -import java.util.*; -import java.util.concurrent.ThreadLocalRandom; - -public class PetData { - private static List breeds = new ArrayList<>(); - static { - breeds.add("Afghan Hound"); - breeds.add("Beagle"); - breeds.add("Bernese Mountain Dog"); - breeds.add("Bloodhound"); - breeds.add("Dalmatian"); - breeds.add("Jack Russell Terrier"); - breeds.add("Norwegian Elkhound"); - } - - private static List names = new ArrayList<>(); - static { - names.add("Bailey"); - names.add("Bella"); - names.add("Max"); - names.add("Lucy"); - names.add("Charlie"); - names.add("Molly"); - names.add("Buddy"); - names.add("Daisy"); - names.add("Rocky"); - names.add("Maggie"); - names.add("Jake"); - names.add("Sophie"); - names.add("Jack"); - names.add("Sadie"); - names.add("Toby"); - names.add("Chloe"); - names.add("Cody"); - names.add("Bailey"); - names.add("Buster"); - names.add("Lola"); - names.add("Duke"); - names.add("Zoe"); - names.add("Cooper"); - names.add("Abby"); - names.add("Riley"); - names.add("Ginger"); - names.add("Harley"); - names.add("Roxy"); - names.add("Bear"); - names.add("Gracie"); - names.add("Tucker"); - names.add("Coco"); - names.add("Murphy"); - names.add("Sasha"); - names.add("Lucky"); - names.add("Lily"); - names.add("Oliver"); - names.add("Angel"); - names.add("Sam"); - names.add("Princess"); - names.add("Oscar"); - names.add("Emma"); - names.add("Teddy"); - names.add("Annie"); - names.add("Winston"); - names.add("Rosie"); - } - - public static List getBreeds() { - return breeds; - } - - public static List getNames() { - return names; - } - - public static String getRandomBreed() { - return breeds.get(ThreadLocalRandom.current().nextInt(0, breeds.size() - 1)); - } - - public static String getRandomName() { - return names.get(ThreadLocalRandom.current().nextInt(0, names.size() - 1)); - } - - public static Date getRandomDoB() { - GregorianCalendar gc = new GregorianCalendar(); - - int year = ThreadLocalRandom.current().nextInt( - Calendar.getInstance().get(Calendar.YEAR) - 15, - Calendar.getInstance().get(Calendar.YEAR) - ); - - gc.set(Calendar.YEAR, year); - - int dayOfYear = ThreadLocalRandom.current().nextInt(1, gc.getActualMaximum(Calendar.DAY_OF_YEAR)); - - gc.set(Calendar.DAY_OF_YEAR, dayOfYear); - return gc.getTime(); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/template.yml b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/template.yml deleted file mode 100644 index 7c5cea2e3..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/sample/pet-store/template.yml +++ /dev/null @@ -1,37 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: Example Pet Store API written with spring-cloud-function web-proxy support - -Globals: - Api: - # API Gateway regional endpoints - EndpointConfiguration: REGIONAL - -Resources: - PetStoreFunction: - Type: AWS::Serverless::Function - Properties: - Handler: org.springframework.cloud.function.adapter.aws.web.WebProxyInvoker::handleRequest - Runtime: java11 - CodeUri: . - MemorySize: 512 - Policies: AWSLambdaBasicExecutionRole - Timeout: 30 - Environment: - Variables: - MAIN_CLASS: oz.spring.petstore.PetStoreSpringAppConfig - Events: - HttpApiEvent: - Type: HttpApi - Properties: - TimeoutInMillis: 20000 - PayloadFormatVersion: '1.0' - -Outputs: - SpringPetStoreApi: - Description: URL for application - Value: !Sub 'https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/pets' - Export: - Name: PetStoreLambda - - diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/FunctionClassUtils.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/FunctionClassUtils.java deleted file mode 100644 index 44d6ef454..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/FunctionClassUtils.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.io.InputStream; -import java.net.URL; -import java.util.Collections; -import java.util.List; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -//import org.springframework.boot.SpringBootConfiguration; -//import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.core.KotlinDetector; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; - -/** - * General utility class which aggregates various class-level utility functions - * used by the framework. - * - * @author Oleg Zhurakousky - * @since 3.0.1 - */ -public final class FunctionClassUtils { - - private static Log logger = LogFactory.getLog(FunctionClassUtils.class); - - private static Class MAIN_CLASS; - - private FunctionClassUtils() { - - } - - /** - * Discovers the start class in the currently running application. - * The discover search order is 'MAIN_CLASS' environment property, - * 'MAIN_CLASS' system property, META-INF/MANIFEST.MF:'Start-Class' attribute, - * meta-inf/manifest.mf:'Start-Class' attribute. - * - * @return instance of Class which represent the start class of the application. - */ - public static Class getStartClass() { - if (MAIN_CLASS == null) { - ClassLoader classLoader = FunctionClassUtils.class.getClassLoader(); - MAIN_CLASS = getStartClass(classLoader); - } - return MAIN_CLASS; - } - - static Class getStartClass(ClassLoader classLoader) { - Class mainClass = null; - if (System.getenv("MAIN_CLASS") != null) { - mainClass = ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader); - } - else if (System.getProperty("MAIN_CLASS") != null) { - mainClass = ClassUtils.resolveClassName(System.getProperty("MAIN_CLASS"), classLoader); - } - else { - try { - Class result = getStartClass( - Collections.list(classLoader.getResources(JarFile.MANIFEST_NAME)), classLoader); - if (result == null) { - result = getStartClass(Collections - .list(classLoader.getResources("meta-inf/manifest.mf")), classLoader); - } - Assert.notNull(result, "Failed to locate main class"); - mainClass = result; - } - catch (Exception ex) { - throw new IllegalStateException("Failed to discover main class. An attempt was made to discover " - + "main class as 'MAIN_CLASS' environment variable, system property as well as " - + "entry in META-INF/MANIFEST.MF (in that order).", ex); - } - } - logger.info("Main class: " + mainClass); - return mainClass; - } - - private static Class getStartClass(List list, ClassLoader classLoader) { - if (logger.isTraceEnabled()) { - logger.trace("Searching manifests: " + list); - } - for (URL url : list) { - try { - InputStream inputStream = null; - Manifest manifest = new Manifest(url.openStream()); - logger.info("Searching for start class in manifest: " + url); - if (logger.isDebugEnabled()) { - manifest.write(System.out); - } - try { - String startClassName = manifest.getMainAttributes().getValue("Start-Class"); - if (!StringUtils.hasText(startClassName)) { - startClassName = manifest.getMainAttributes().getValue("Main-Class"); - } - - if (StringUtils.hasText(startClassName)) { - Class startClass = ClassUtils.forName(startClassName, classLoader); - - if (KotlinDetector.isKotlinType(startClass)) { - PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver(classLoader); - String packageName = startClass.getPackage().getName(); - Resource[] resources = r.getResources("classpath:" + packageName.replace(".", "/") + "/*.class"); - for (int i = 0; i < resources.length; i++) { - Resource resource = resources[i]; - String className = packageName + "." + (resource.getFilename().replace("/", ".")).replace(".class", ""); - startClass = ClassUtils.forName(className, classLoader); - } - } - } - } - finally { - if (inputStream != null) { - inputStream.close(); - } - } - } - catch (Exception ex) { - logger.debug("Failed to determine Start-Class in manifest file of " + url, ex); - } - } - return null; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyAsyncContext.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyAsyncContext.java deleted file mode 100644 index 5078a7491..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyAsyncContext.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import jakarta.servlet.AsyncContext; -import jakarta.servlet.AsyncEvent; -import jakarta.servlet.AsyncListener; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import org.springframework.beans.BeanUtils; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.web.util.WebUtils; - -/** - * Implementation of Async context for {@link ProxyMvc}. - * - * @author Oleg Zhurakousky - */ -public class ProxyAsyncContext implements AsyncContext { - private final HttpServletRequest request; - - @Nullable - private final HttpServletResponse response; - - private final List listeners = new ArrayList<>(); - - @Nullable - private String dispatchedPath; - - private long timeout = 10 * 1000L; - - private final List dispatchHandlers = new ArrayList<>(); - - - public ProxyAsyncContext(ServletRequest request, @Nullable ServletResponse response) { - this.request = (HttpServletRequest) request; - this.response = (HttpServletResponse) response; - } - - - public void addDispatchHandler(Runnable handler) { - Assert.notNull(handler, "Dispatch handler must not be null"); - synchronized (this) { - if (this.dispatchedPath == null) { - this.dispatchHandlers.add(handler); - } - else { - handler.run(); - } - } - } - - @Override - public ServletRequest getRequest() { - return this.request; - } - - @Override - @Nullable - public ServletResponse getResponse() { - return this.response; - } - - @Override - public boolean hasOriginalRequestAndResponse() { - return (this.request instanceof ProxyHttpServletRequest && this.response instanceof ProxyHttpServletResponse); - } - - @Override - public void dispatch() { - dispatch(this.request.getRequestURI()); - } - - @Override - public void dispatch(String path) { - dispatch(null, path); - } - - @Override - public void dispatch(@Nullable ServletContext context, String path) { - synchronized (this) { - this.dispatchedPath = path; - this.dispatchHandlers.forEach(Runnable::run); - } - } - - @Nullable - public String getDispatchedPath() { - return this.dispatchedPath; - } - - @Override - public void complete() { - ProxyHttpServletRequest mockRequest = WebUtils.getNativeRequest(this.request, ProxyHttpServletRequest.class); - if (mockRequest != null) { - mockRequest.setAsyncStarted(false); - } - for (AsyncListener listener : this.listeners) { - try { - listener.onComplete(new AsyncEvent(this, this.request, this.response)); - } - catch (IOException ex) { - throw new IllegalStateException("AsyncListener failure", ex); - } - } - } - - @Override - public void start(Runnable runnable) { - runnable.run(); - } - - @Override - public void addListener(AsyncListener listener) { - this.listeners.add(listener); - } - - @Override - public void addListener(AsyncListener listener, ServletRequest request, ServletResponse response) { - this.listeners.add(listener); - } - - public List getListeners() { - return this.listeners; - } - - @Override - public T createListener(Class clazz) throws ServletException { - return BeanUtils.instantiateClass(clazz); - } - - /** - * By default this is set to 10000 (10 seconds) even though the Servlet API - * specifies a default async request timeout of 30 seconds. Keep in mind the - * timeout could further be impacted by global configuration through the MVC - * Java config or the XML namespace, as well as be overridden per request on - * {@link org.springframework.web.context.request.async.DeferredResult DeferredResult} - * or on - * {@link org.springframework.web.servlet.mvc.method.annotation.SseEmitter SseEmitter}. - * @param timeout the timeout value to use. - * @see AsyncContext#setTimeout(long) - */ - @Override - public void setTimeout(long timeout) { - this.timeout = timeout; - } - - @Override - public long getTimeout() { - return this.timeout; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyErrorController.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyErrorController.java deleted file mode 100644 index a3de30931..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyErrorController.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import jakarta.servlet.RequestDispatcher; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.view.json.MappingJackson2JsonView; - -/** - * - * @author Oleg Zhurakousky - * - */ -@Controller -@RequestMapping("/error") -public class ProxyErrorController { - - private final SimpleDateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z"); - - private final MappingJackson2JsonView view = new MappingJackson2JsonView(); - - @RequestMapping(produces = MediaType.TEXT_HTML_VALUE) - public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { - HttpStatus status = getStatus(request); - Map model = new HashMap<>(); - model.put("status", response.getStatus()); - model.put("error", request.getAttribute(RequestDispatcher.ERROR_MESSAGE)); - model.put("path", request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI)); - model.put("timestamp", df.format(new Date())); - response.setStatus(status.value()); - ModelAndView modelAndView = resolveErrorView(request, response, status, model); - return (modelAndView != null) ? modelAndView : new ModelAndView("error", model); - } - - protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, - Map model) { - ModelAndView modelAndView = new ModelAndView("Whitelabel Error Page", model); - modelAndView.setStatus(status); - modelAndView.setView(this.view); - return modelAndView; - } - - protected HttpStatus getStatus(HttpServletRequest request) { - Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); - if (statusCode == null) { - return HttpStatus.INTERNAL_SERVER_ERROR; - } - try { - return HttpStatus.valueOf(statusCode); - } - catch (Exception ex) { - return HttpStatus.INTERNAL_SERVER_ERROR; - } - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyFilterRegistration.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyFilterRegistration.java deleted file mode 100644 index 43e20212f..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyFilterRegistration.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.util.Collection; -import java.util.EnumSet; -import java.util.Map; -import java.util.Set; - -import jakarta.servlet.DispatcherType; -import jakarta.servlet.Filter; -import jakarta.servlet.FilterRegistration; - -/** - * - * @author Oleg Zhurakousky - * @since 4.x - * - */ -public class ProxyFilterRegistration implements FilterRegistration, FilterRegistration.Dynamic { - - public Filter getFilter() { - return filter; - } - - private final String name; - - private final Filter filter; - - public ProxyFilterRegistration(String name, Filter filter) { - this.name = name; - this.filter = filter; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public String getClassName() { - return this.filter.getClass().getName(); - } - - @Override - public boolean setInitParameter(String name, String value) { - // TODO Auto-generated method stub - return false; - } - - @Override - public String getInitParameter(String name) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Set setInitParameters(Map initParameters) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Map getInitParameters() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setAsyncSupported(boolean isAsyncSupported) { - // TODO Auto-generated method stub - - } - - @Override - public void addMappingForServletNames(EnumSet dispatcherTypes, boolean isMatchAfter, - String... servletNames) { - // TODO Auto-generated method stub - - } - - @Override - public Collection getServletNameMappings() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void addMappingForUrlPatterns(EnumSet dispatcherTypes, boolean isMatchAfter, - String... urlPatterns) { - // TODO Auto-generated method stub - - } - - @Override - public Collection getUrlPatternMappings() { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletRequest.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletRequest.java deleted file mode 100644 index 3be55e479..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletRequest.java +++ /dev/null @@ -1,1013 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.security.Principal; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; - -import jakarta.servlet.AsyncContext; -import jakarta.servlet.DispatcherType; -import jakarta.servlet.ReadListener; -import jakarta.servlet.RequestDispatcher; -import jakarta.servlet.ServletConnection; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletInputStream; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; -import jakarta.servlet.http.HttpUpgradeHandler; -import jakarta.servlet.http.Part; - -import org.springframework.http.HttpHeaders; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class ProxyHttpServletRequest implements HttpServletRequest { - - private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); - - private static final BufferedReader EMPTY_BUFFERED_READER = new BufferedReader(new StringReader("")); - - /** - * Date formats as specified in the HTTP RFC. - * - * @see Section - * 7.1.1.1 of RFC 7231 - */ - private static final String[] DATE_FORMATS = new String[] { "EEE, dd MMM yyyy HH:mm:ss zzz", - "EEE, dd-MMM-yy HH:mm:ss zzz", "EEE MMM dd HH:mm:ss yyyy" }; - - private final ServletContext servletContext; - - // --------------------------------------------------------------------- - // ServletRequest properties - // --------------------------------------------------------------------- - - private final Map attributes = new LinkedHashMap<>(); - - @Nullable - private String characterEncoding; - - @Nullable - private byte[] content; - - @Nullable - private ServletInputStream inputStream; - - @Nullable - private BufferedReader reader; - - private final Map parameters = new LinkedHashMap<>(16); - - /** List of locales in descending order. */ - private final LinkedList locales = new LinkedList<>(); - - private boolean asyncStarted = false; - - private boolean asyncSupported = true; - - private DispatcherType dispatcherType = DispatcherType.REQUEST; - - @Nullable - private String authType; - - @Nullable - private Cookie[] cookies; - - private final HttpHeaders headers = new HttpHeaders(); - - @Nullable - private String method; - - @Nullable - private String pathInfo; - - private String contextPath = ""; - - @Nullable - private String queryString; - - @Nullable - private String remoteUser; - - private final Set userRoles = new HashSet<>(); - - @Nullable - private Principal userPrincipal; - - @Nullable - private String requestedSessionId; - - @Nullable - private String requestURI; - - private String servletPath = ""; - - @Nullable - private HttpSession session; - - private boolean requestedSessionIdValid = true; - - private boolean requestedSessionIdFromCookie = true; - - private boolean requestedSessionIdFromURL = false; - - private final MultiValueMap parts = new LinkedMultiValueMap<>(); - - private AsyncContext asyncContext; - - public ProxyHttpServletRequest(ServletContext servletContext, String method, String requestURI) { - this.servletContext = servletContext; - this.method = method; - this.requestURI = requestURI; - this.locales.add(Locale.ENGLISH); - } - - /** - * Return the ServletContext that this request is associated with. (Not - * available in the standard HttpServletRequest interface for some reason.) - */ - @Override - public ServletContext getServletContext() { - return this.servletContext; - } - - @Override - public Object getAttribute(String name) { - return this.attributes.get(name); - } - - @Override - public Enumeration getAttributeNames() { - return Collections.enumeration(new LinkedHashSet<>(this.attributes.keySet())); - } - - @Override - @Nullable - public String getCharacterEncoding() { - return this.characterEncoding; - } - - @Override - public void setCharacterEncoding(@Nullable String characterEncoding) { - this.characterEncoding = characterEncoding; - } - - /** - * Set the content of the request body as a byte array. - *

- * If the supplied byte array represents text such as XML or JSON, the - * {@link #setCharacterEncoding character encoding} should typically be set as - * well. - * - * @see #setCharacterEncoding(String) - * @see #getContentAsByteArray() - * @see #getContentAsString() - */ - public void setContent(@Nullable byte[] content) { - this.content = content; - this.inputStream = null; - this.reader = null; - } - - /** - * Get the content of the request body as a byte array. - * - * @return the content as a byte array (potentially {@code null}) - * @since 5.0 - * @see #setContent(byte[]) - * @see #getContentAsString() - */ - @Nullable - public byte[] getContentAsByteArray() { - return this.content; - } - - /** - * Get the content of the request body as a {@code String}, using the configured - * {@linkplain #getCharacterEncoding character encoding}. - * - * @return the content as a {@code String}, potentially {@code null} - * @throws IllegalStateException if the character encoding has not been - * set - * @throws UnsupportedEncodingException if the character encoding is not - * supported - * @since 5.0 - * @see #setContent(byte[]) - * @see #setCharacterEncoding(String) - * @see #getContentAsByteArray() - */ - @Nullable - public String getContentAsString() throws IllegalStateException, UnsupportedEncodingException { - - if (this.content == null) { - return null; - } - return new String(this.content, StandardCharsets.UTF_8); - } - - @Override - public int getContentLength() { - return (this.content != null ? this.content.length : -1); - } - - @Override - public long getContentLengthLong() { - return getContentLength(); - } - - public void setContentType(@Nullable String contentType) { - this.headers.set(HttpHeaders.CONTENT_TYPE, contentType); - } - - @Override - @Nullable - public String getContentType() { - return this.headers.containsKey(HttpHeaders.CONTENT_TYPE) ? this.headers.get(HttpHeaders.CONTENT_TYPE).get(0) : null; - } - - @Override - public ServletInputStream getInputStream() { - InputStream stream = new ByteArrayInputStream(this.content); - return new ServletInputStream() { - - boolean finished = false; - - @Override - public int read() throws IOException { - int readByte = stream.read(); - if (readByte == -1) { - finished = true; - } - return readByte; - } - - @Override - public void setReadListener(ReadListener readListener) { - } - - @Override - public boolean isReady() { - return !finished; - } - - @Override - public boolean isFinished() { - return finished; - } - }; - } - - /** - * Set a single value for the specified HTTP parameter. - *

- * If there are already one or more values registered for the given parameter - * name, they will be replaced. - */ - public void setParameter(String name, String value) { - setParameter(name, new String[] { value }); - } - - /** - * Set an array of values for the specified HTTP parameter. - *

- * If there are already one or more values registered for the given parameter - * name, they will be replaced. - */ - public void setParameter(String name, String... values) { - Assert.notNull(name, "Parameter name must not be null"); - this.parameters.put(name, values); - } - - /** - * Set all provided parameters replacing any existing values - * for the provided parameter names. To add without replacing existing values, - * use {@link #addParameters(java.util.Map)}. - */ - public void setParameters(Map params) { - Assert.notNull(params, "Parameter map must not be null"); - params.forEach((key, value) -> { - if (value instanceof String) { - setParameter(key, (String) value); - } - else if (value instanceof String[]) { - setParameter(key, (String[]) value); - } - else { - throw new IllegalArgumentException("Parameter map value must be single value " + " or array of type [" - + String.class.getName() + "]"); - } - }); - } - - /** - * Add a single value for the specified HTTP parameter. - *

- * If there are already one or more values registered for the given parameter - * name, the given value will be added to the end of the list. - */ - public void addParameter(String name, @Nullable String value) { - addParameter(name, new String[] { value }); - } - - /** - * Add an array of values for the specified HTTP parameter. - *

- * If there are already one or more values registered for the given parameter - * name, the given values will be added to the end of the list. - */ - public void addParameter(String name, String... values) { - Assert.notNull(name, "Parameter name must not be null"); - String[] oldArr = this.parameters.get(name); - if (oldArr != null) { - String[] newArr = new String[oldArr.length + values.length]; - System.arraycopy(oldArr, 0, newArr, 0, oldArr.length); - System.arraycopy(values, 0, newArr, oldArr.length, values.length); - this.parameters.put(name, newArr); - } - else { - this.parameters.put(name, values); - } - } - - /** - * Add all provided parameters without replacing any existing - * values. To replace existing values, use - * {@link #setParameters(java.util.Map)}. - */ - public void addParameters(Map params) { - Assert.notNull(params, "Parameter map must not be null"); - params.forEach((key, value) -> { - if (value instanceof String) { - addParameter(key, (String) value); - } - else if (value instanceof String[]) { - addParameter(key, (String[]) value); - } - else { - throw new IllegalArgumentException("Parameter map value must be single value " + " or array of type [" - + String.class.getName() + "]"); - } - }); - } - - /** - * Remove already registered values for the specified HTTP parameter, if any. - */ - public void removeParameter(String name) { - Assert.notNull(name, "Parameter name must not be null"); - this.parameters.remove(name); - } - - /** - * Remove all existing parameters. - */ - public void removeAllParameters() { - this.parameters.clear(); - } - - @Override - @Nullable - public String getParameter(String name) { - Assert.notNull(name, "Parameter name must not be null"); - String[] arr = this.parameters.get(name); - return (arr != null && arr.length > 0 ? arr[0] : null); - } - - @Override - public Enumeration getParameterNames() { - return Collections.enumeration(this.parameters.keySet()); - } - - @Override - public String[] getParameterValues(String name) { - Assert.notNull(name, "Parameter name must not be null"); - return this.parameters.get(name); - } - - @Override - public Map getParameterMap() { - return Collections.unmodifiableMap(this.parameters); - } - - @Override - public String getProtocol() { - throw new UnsupportedOperationException(); - } - - @Override - public String getScheme() { - return "https"; - } - - public void setServerName(String serverName) { - throw new UnsupportedOperationException(); - } - - @Override - public String getServerName() { - return "spring-serverless-web-proxy"; - } - - public void setServerPort(int serverPort) { - throw new UnsupportedOperationException(); - } - - @Override - public int getServerPort() { - return 0; - } - - @Override - public BufferedReader getReader() throws UnsupportedEncodingException { - if (this.reader != null) { - return this.reader; - } - else if (this.inputStream != null) { - throw new IllegalStateException( - "Cannot call getReader() after getInputStream() has already been called for the current request"); - } - - if (this.content != null) { - InputStream sourceStream = new ByteArrayInputStream(this.content); - Reader sourceReader = (this.characterEncoding != null) - ? new InputStreamReader(sourceStream, this.characterEncoding) - : new InputStreamReader(sourceStream); - this.reader = new BufferedReader(sourceReader); - } - else { - this.reader = EMPTY_BUFFERED_READER; - } - return this.reader; - } - - public void setRemoteAddr(String remoteAddr) { - throw new UnsupportedOperationException(); - } - - @Override - public String getRemoteAddr() { - return "proxy"; - } - - public void setRemoteHost(String remoteHost) { - throw new UnsupportedOperationException(); - } - - @Override - public String getRemoteHost() { - throw new UnsupportedOperationException(); - } - - @Override - public void setAttribute(String name, @Nullable Object value) { - Assert.notNull(name, "Attribute name must not be null"); - if (value != null) { - this.attributes.put(name, value); - } - else { - this.attributes.remove(name); - } - } - - @Override - public void removeAttribute(String name) { - Assert.notNull(name, "Attribute name must not be null"); - this.attributes.remove(name); - } - - /** - * Clear all of this request's attributes. - */ - public void clearAttributes() { - this.attributes.clear(); - } - - /** - * Return the first preferred {@linkplain Locale locale} configured in this mock - * request. - *

- * If no locales have been explicitly configured, the default, preferred - * {@link Locale} for the server mocked by this request is - * {@link Locale#ENGLISH}. - *

- * In contrast to the Servlet specification, this mock implementation does - * not take into consideration any locales specified via the - * {@code Accept-Language} header. - * - * @see javax.servlet.ServletRequest#getLocale() - * @see #addPreferredLocale(Locale) - * @see #setPreferredLocales(List) - */ - @Override - public Locale getLocale() { - return this.locales.getFirst(); - } - - /** - * Return an {@linkplain Enumeration enumeration} of the preferred - * {@linkplain Locale locales} configured in this mock request. - *

- * If no locales have been explicitly configured, the default, preferred - * {@link Locale} for the server mocked by this request is - * {@link Locale#ENGLISH}. - *

- * In contrast to the Servlet specification, this mock implementation does - * not take into consideration any locales specified via the - * {@code Accept-Language} header. - * - * @see javax.servlet.ServletRequest#getLocales() - * @see #addPreferredLocale(Locale) - * @see #setPreferredLocales(List) - */ - @Override - public Enumeration getLocales() { - return Collections.enumeration(this.locales); - } - - /** - * Return {@code true} if the {@link #setSecure secure} flag has been set to - * {@code true} or if the {@link #getScheme scheme} is {@code https}. - * - * @see javax.servlet.ServletRequest#isSecure() - */ - @Override - public boolean isSecure() { - return false; - } - - @Override - public RequestDispatcher getRequestDispatcher(String path) { - throw new UnsupportedOperationException(); - } - - public void setRemotePort(int remotePort) { - throw new UnsupportedOperationException(); - } - - @Override - public int getRemotePort() { - throw new UnsupportedOperationException(); - } - - public void setLocalName(String localName) { - throw new UnsupportedOperationException(); - } - - @Override - public String getLocalName() { - throw new UnsupportedOperationException(); - } - - public void setLocalAddr(String localAddr) { - throw new UnsupportedOperationException(); - } - - @Override - public String getLocalAddr() { - return "proxy"; - } - - public void setLocalPort(int localPort) { - throw new UnsupportedOperationException(); - } - - @Override - public int getLocalPort() { - throw new UnsupportedOperationException(); - } - - @Override - public AsyncContext startAsync() { - return startAsync(this, null); - } - - @Override - public AsyncContext startAsync(ServletRequest request, @Nullable ServletResponse response) { - Assert.state(this.asyncSupported, "Async not supported"); - this.dispatcherType = DispatcherType.ASYNC; - this.asyncStarted = true; - this.asyncContext = this.asyncContext == null ? new ProxyAsyncContext(request, response) : this.asyncContext; - return this.asyncContext; - } - - public void setAsyncStarted(boolean asyncStarted) { - this.asyncStarted = asyncStarted; - } - - @Override - public boolean isAsyncStarted() { - return this.asyncStarted; - } - - public void setAsyncSupported(boolean asyncSupported) { - this.asyncSupported = asyncSupported; - this.dispatcherType = DispatcherType.ASYNC; - } - - @Override - public boolean isAsyncSupported() { - return this.asyncSupported; - } - - public void setAsyncContext(@Nullable AsyncContext asyncContext) { - this.asyncContext = asyncContext; - } - - @Override - @Nullable - public AsyncContext getAsyncContext() { - return this.asyncContext; - } - - - public void setDispatcherType(DispatcherType dispatcherType) { - this.dispatcherType = dispatcherType; - } - - @Override - public DispatcherType getDispatcherType() { - return this.dispatcherType; - } - - public void setAuthType(@Nullable String authType) { - this.authType = authType; - } - - @Override - @Nullable - public String getAuthType() { - return this.authType; - } - - @Override - @Nullable - public Cookie[] getCookies() { - return this.cookies; - } - - @Override - @Nullable - public String getHeader(String name) { - return this.headers.containsKey(name) ? this.headers.get(name).get(0) : null; - } - - @Override - public Enumeration getHeaders(String name) { - return Collections.enumeration(this.headers.containsKey(name) ? this.headers.get(name) : new LinkedList<>()); - } - - @Override - public Enumeration getHeaderNames() { - return Collections.enumeration(this.headers.keySet()); - } - - public void setHeader(String name, @Nullable String value) { - this.headers.set(name, value); - } - - public void addHeader(String name, @Nullable String value) { - this.headers.add(name, value); - } - - public void addHeaders(MultiValueMap headers) { - this.headers.addAll(headers); - } - - public void setHeaders(MultiValueMap headers) { - this.headers.clear(); - this.addHeaders(headers); - } - - @Override - public int getIntHeader(String name) { - List header = this.headers.get(name); - if (!CollectionUtils.isEmpty(header) && header.size() == 1) { - Object value = header.get(0); - if (value instanceof Number) { - return ((Number) value).intValue(); - } - else if (value instanceof String) { - return Integer.parseInt((String) value); - } - else if (value != null) { - throw new NumberFormatException("Value for header '" + name + "' is not a Number: " + value); - } - else { - return -1; - } - } - else { - return -1; - } - } - - @Override - public long getDateHeader(String name) { - List header = this.headers.get(name); - if (!CollectionUtils.isEmpty(header) && header.size() == 1) { - Object value = header.get(0); - if (value instanceof Date) { - return ((Date) value).getTime(); - } - else if (value instanceof Number) { - return ((Number) value).longValue(); - } - else if (value instanceof String) { - return parseDateHeader(name, (String) value); - } - else if (value != null) { - throw new IllegalArgumentException( - "Value for header '" + name + "' is not a Date, Number, or String: " + value); - } - else { - return -1L; - } - } - else { - return -1; - } - } - - private long parseDateHeader(String name, String value) { - for (String dateFormat : DATE_FORMATS) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US); - simpleDateFormat.setTimeZone(GMT); - try { - return simpleDateFormat.parse(value).getTime(); - } - catch (ParseException ex) { - // ignore - } - } - throw new IllegalArgumentException("Cannot parse date value '" + value + "' for '" + name + "' header"); - } - - public void setMethod(@Nullable String method) { - this.method = method; - } - - @Override - @Nullable - public String getMethod() { - return this.method; - } - - public void setPathInfo(@Nullable String pathInfo) { - this.pathInfo = pathInfo; - } - - @Override - @Nullable - public String getPathInfo() { - return this.pathInfo; - } - - @Override - @Nullable - public String getPathTranslated() { - //return (this.pathInfo != null ? getRealPath(this.pathInfo) : null); - return this.pathInfo; - } - - public void setContextPath(String contextPath) { - this.contextPath = contextPath; - } - - @Override - public String getContextPath() { - return this.contextPath; - } - - public void setQueryString(@Nullable String queryString) { - this.queryString = queryString; - } - - @Override - @Nullable - public String getQueryString() { - return this.queryString; - } - - public void setRemoteUser(@Nullable String remoteUser) { - this.remoteUser = remoteUser; - } - - @Override - @Nullable - public String getRemoteUser() { - return this.remoteUser; - } - - public void addUserRole(String role) { - this.userRoles.add(role); - } - - @Override - public boolean isUserInRole(String role) { - throw new UnsupportedOperationException(); - } - - public void setUserPrincipal(@Nullable Principal userPrincipal) { - this.userPrincipal = userPrincipal; - } - - @Override - @Nullable - public Principal getUserPrincipal() { - return this.userPrincipal; - } - - public void setRequestedSessionId(@Nullable String requestedSessionId) { - this.requestedSessionId = requestedSessionId; - } - - @Override - @Nullable - public String getRequestedSessionId() { - return this.requestedSessionId; - } - - public void setRequestURI(@Nullable String requestURI) { - this.requestURI = requestURI; - } - - @Override - @Nullable - public String getRequestURI() { - return this.requestURI; - } - - @Override - public StringBuffer getRequestURL() { - return new StringBuffer(this.requestURI); - } - - public void setServletPath(String servletPath) { - this.servletPath = servletPath; - } - - @Override - public String getServletPath() { - return this.servletPath; - } - - public void setSession(HttpSession session) { - throw new UnsupportedOperationException(); - } - - @Override - @Nullable - public HttpSession getSession(boolean create) { - if (this.session == null) { - this.session = new ProxyHttpSession(this.servletContext); - } - return this.session; - } - - @Override - @Nullable - public HttpSession getSession() { - return getSession(true); - } - - @Override - public String changeSessionId() { - throw new UnsupportedOperationException(); - } - - public void setRequestedSessionIdValid(boolean requestedSessionIdValid) { - this.requestedSessionIdValid = requestedSessionIdValid; - } - - @Override - public boolean isRequestedSessionIdValid() { - return this.requestedSessionIdValid; - } - - public void setRequestedSessionIdFromCookie(boolean requestedSessionIdFromCookie) { - this.requestedSessionIdFromCookie = requestedSessionIdFromCookie; - } - - @Override - public boolean isRequestedSessionIdFromCookie() { - return this.requestedSessionIdFromCookie; - } - - public void setRequestedSessionIdFromURL(boolean requestedSessionIdFromURL) { - this.requestedSessionIdFromURL = requestedSessionIdFromURL; - } - - @Override - public boolean isRequestedSessionIdFromURL() { - return this.requestedSessionIdFromURL; - } - - @Override - public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { - throw new UnsupportedOperationException(); - } - - @Override - public void login(String username, String password) throws ServletException { - throw new UnsupportedOperationException(); - } - - @Override - public void logout() throws ServletException { - this.userPrincipal = null; - this.remoteUser = null; - this.authType = null; - } - - public void addPart(Part part) { - this.parts.add(part.getName(), part); - } - - @Override - @Nullable - public Part getPart(String name) throws IOException, ServletException { - return this.parts.getFirst(name); - } - - @Override - public Collection getParts() throws IOException, ServletException { - List result = new LinkedList<>(); - for (List list : this.parts.values()) { - result.addAll(list); - } - return result; - } - - @Override - public T upgrade(Class handlerClass) throws IOException, ServletException { - throw new UnsupportedOperationException(); - } - - @Override - public String getRequestId() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getProtocolRequestId() { - // TODO Auto-generated method stub - return null; - } - - @Override - public ServletConnection getServletConnection() { - // TODO Auto-generated method stub - return null; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java deleted file mode 100644 index 12575af1d..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.nio.charset.Charset; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Locale; - -import jakarta.servlet.ServletOutputStream; -import jakarta.servlet.WriteListener; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletResponse; - -import org.springframework.http.HttpHeaders; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.web.util.WebUtils; - - -/** - * - * @author Oleg Zhurakousky - * @since 4.x - * - */ -public class ProxyHttpServletResponse implements HttpServletResponse { - - private static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; - - private String defaultCharacterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING; - - private String characterEncoding = this.defaultCharacterEncoding; - - private final ByteArrayOutputStream content = new ByteArrayOutputStream(1024); - - private final ServletOutputStream outputStream = new ResponseServletOutputStream(); - - private String contentType; - - private int bufferSize = 4096; - - private Locale locale = Locale.getDefault(); - - private final List cookies = new ArrayList<>(); - - private final HttpHeaders headers = new HttpHeaders(); - - private int status = HttpServletResponse.SC_OK; - - private ResponsePrintWriter writer; - - @Nullable - private String errorMessage; - - @Override - public void setCharacterEncoding(String characterEncoding) { - this.characterEncoding = characterEncoding; - } - - @Override - public String getCharacterEncoding() { - return this.characterEncoding; - } - - @Override - public ServletOutputStream getOutputStream() { - return this.outputStream; - } - - @Override - public PrintWriter getWriter() throws UnsupportedEncodingException { - if (this.writer == null) { - Writer targetWriter = new OutputStreamWriter(this.content, getCharacterEncoding()); - this.writer = new ResponsePrintWriter(targetWriter); - } - return this.writer; - } - - public byte[] getContentAsByteArray() { - return this.content.toByteArray(); - } - - /** - * Get the content of the response body as a {@code String}, using the charset - * specified for the response by the application, either through - * {@link HttpServletResponse} methods or through a charset parameter on the - * {@code Content-Type}. If no charset has been explicitly defined, the - * {@linkplain #setDefaultCharacterEncoding(String) default character encoding} - * will be used. - * - * @return the content as a {@code String} - * @throws UnsupportedEncodingException if the character encoding is not - * supported - * @see #getContentAsString(Charset) - * @see #setCharacterEncoding(String) - * @see #setContentType(String) - */ - public String getContentAsString() throws UnsupportedEncodingException { - return this.content.toString(getCharacterEncoding()); - } - - public String getContentAsString(Charset fallbackCharset) throws UnsupportedEncodingException { - return this.content.toString(getCharacterEncoding()); - } - - @Override - public void setContentLength(int contentLength) { - throw new UnsupportedOperationException(); - } - - @Override - public void setContentLengthLong(long len) { - // Ignore - } - - @Override - public void setContentType(@Nullable String contentType) { - this.contentType = contentType; - } - - @Override - @Nullable - public String getContentType() { - return this.contentType; - } - - @Override - public void setBufferSize(int bufferSize) { - this.bufferSize = bufferSize; - } - - @Override - public int getBufferSize() { - return this.bufferSize; - } - - @Override - public void flushBuffer() { - } - - @Override - public void resetBuffer() { - Assert.state(!isCommitted(), "Cannot reset buffer - response is already committed"); - this.content.reset(); - } - - @Override - public boolean isCommitted() { - return this.writer == null ? false : this.writer.commited; - } - - @Override - public void reset() { - resetBuffer(); - this.characterEncoding = this.defaultCharacterEncoding; - this.contentType = null; - this.locale = Locale.getDefault(); - this.cookies.clear(); - this.headers.clear(); - this.status = HttpServletResponse.SC_OK; - this.errorMessage = null; - } - - @Override - public void setLocale(@Nullable Locale locale) { - if (locale == null) { - return; - } - this.locale = locale; - this.headers.add(HttpHeaders.CONTENT_LANGUAGE, locale.toLanguageTag()); - } - - @Override - public Locale getLocale() { - return this.locale; - } - - // --------------------------------------------------------------------- - // HttpServletResponse interface - // --------------------------------------------------------------------- - - @Override - public void addCookie(Cookie cookie) { - throw new UnsupportedOperationException(); - } - - @Nullable - public Cookie getCookie(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean containsHeader(String name) { - return this.headers.containsKey(name); - } - - /** - * Return the names of all specified headers as a Set of Strings. - *

- * As of Servlet 3.0, this method is also defined in - * {@link HttpServletResponse}. - * - * @return the {@code Set} of header name {@code Strings}, or an empty - * {@code Set} if none - */ - @Override - public Collection getHeaderNames() { - return this.headers.keySet(); - } - - /** - * Return the primary value for the given header as a String, if any. Will - * return the first value in case of multiple values. - *

- * As of Servlet 3.0, this method is also defined in - * {@link HttpServletResponse}. As of Spring 3.1, it returns a stringified value - * for Servlet 3.0 compatibility. Consider using {@link #getHeaderValue(String)} - * for raw Object access. - * - * @param name the name of the header - * @return the associated header value, or {@code null} if none - */ - @Override - @Nullable - public String getHeader(String name) { - return this.headers.containsKey(name) ? this.headers.get(name).get(0) : null; - } - - /** - * Return all values for the given header as a List of Strings. - *

- * As of Servlet 3.0, this method is also defined in - * {@link HttpServletResponse}. As of Spring 3.1, it returns a List of - * stringified values for Servlet 3.0 compatibility. Consider using - * {@link #getHeaderValues(String)} for raw Object access. - * - * @param name the name of the header - * @return the associated header values, or an empty List if none - */ - @Override - public List getHeaders(String name) { - if (!this.headers.containsKey(name)) { - return Collections.emptyList(); - } - return this.headers.get(name); - } - - /** - * Return the primary value for the given header, if any. - *

- * Will return the first value in case of multiple values. - * - * @param name the name of the header - * @return the associated header value, or {@code null} if none - */ - @Nullable - public Object getHeaderValue(String name) { - return this.headers.containsKey(name) ? this.headers.get(name).get(0) : null; - } - - /** - * The default implementation returns the given URL String as-is. - *

- * Can be overridden in subclasses, appending a session id or the like. - */ - @Override - public String encodeURL(String url) { - return url; - } - - /** - * The default implementation delegates to {@link #encodeURL}, returning the - * given URL String as-is. - *

- * Can be overridden in subclasses, appending a session id or the like in a - * redirect-specific fashion. For general URL encoding rules, override the - * common {@link #encodeURL} method instead, applying to redirect URLs as well - * as to general URLs. - */ - @Override - public String encodeRedirectURL(String url) { - return encodeURL(url); - } - - @Override - public void sendError(int status, String errorMessage) throws IOException { - Assert.state(!isCommitted(), "Cannot set error status - response is already committed"); - this.status = status; - this.errorMessage = errorMessage; - } - - @Override - public void sendError(int status) throws IOException { - Assert.state(!isCommitted(), "Cannot set error status - response is already committed"); - this.status = status; - } - - @Override - public void sendRedirect(String url) throws IOException { - Assert.state(!isCommitted(), "Cannot send redirect - response is already committed"); - Assert.notNull(url, "Redirect URL must not be null"); - setHeader(HttpHeaders.LOCATION, url); - setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - } - - @Nullable - public String getRedirectedUrl() { - return getHeader(HttpHeaders.LOCATION); - } - - @Override - public void setDateHeader(String name, long value) { - this.headers.set(name, formatDate(value)); - } - - @Override - public void addDateHeader(String name, long value) { - this.headers.add(name, formatDate(value)); - } - - private String formatDate(long date) { - return newDateFormat().format(new Date(date)); - } - - private DateFormat newDateFormat() { - SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.US); - return dateFormat; - } - - @Override - public void setHeader(String name, @Nullable String value) { - this.headers.set(name, value); - } - - @Override - public void addHeader(String name, @Nullable String value) { - this.headers.add(name, value); - } - - @Override - public void setIntHeader(String name, int value) { - this.headers.set(name, String.valueOf(value)); - } - - @Override - public void addIntHeader(String name, int value) { - this.headers.add(name, String.valueOf(value)); - } - - @Override - public void setStatus(int status) { - if (!this.isCommitted()) { - this.status = status; - } - } - - - @Override - public int getStatus() { - return this.status; - } - - @Nullable - public String getErrorMessage() { - return this.errorMessage; - } - - /** - * Inner class that adapts the ServletOutputStream to mark the response as - * committed once the buffer size is exceeded. - */ - private class ResponseServletOutputStream extends ServletOutputStream { - - private WriteListener listener; - - @Override - public boolean isReady() { - return true; - } - - @Override - public void setWriteListener(WriteListener writeListener) { - if (writeListener != null) { - try { - writeListener.onWritePossible(); - } - catch (IOException e) { - // log.error("Output stream is not writable", e); - } - - listener = writeListener; - } - } - - @Override - public void write(int b) throws IOException { - try { - content.write(b); - } - catch (Exception e) { - if (listener != null) { - listener.onError(e); - } - } - } - - @Override - public void close() throws IOException { - super.close(); - flushBuffer(); - } - } - - private class ResponsePrintWriter extends PrintWriter { - - private boolean commited; - - ResponsePrintWriter(Writer out) { - super(out, true); - } - - @Override - public void write(char[] buf, int off, int len) { - super.write(buf, off, len); - super.flush(); - this.commited = true; - } - - @Override - public void write(String s, int off, int len) { - super.write(s, off, len); - super.flush(); - this.commited = true; - } - - @Override - public void write(int c) { - super.write(c); - super.flush(); - this.commited = true; - } - - @Override - public void flush() { - super.flush(); - this.commited = true; - } - - @Override - public void close() { - super.flush(); - super.close(); - this.commited = true; - } - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpSession.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpSession.java deleted file mode 100644 index 0e41f4836..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpSession.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.util.Collections; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import jakarta.servlet.ServletContext; -import jakarta.servlet.http.HttpSession; - -/** - * - * @author Oleg Zhurakousky - * @since 4.x - * - * - */ -public class ProxyHttpSession implements HttpSession { - - private final long creationTime; - - private final String sessionId; - - private final ServletContext servletContext; - - private final Map attributes = new HashMap<>(); - - public ProxyHttpSession(ServletContext servletContext) { - this.creationTime = new Date().getTime(); - this.sessionId = UUID.randomUUID().toString(); - this.servletContext = servletContext; - } - - @Override - public long getCreationTime() { - return this.creationTime; - } - - @Override - public String getId() { - return this.sessionId; - } - - @Override - public long getLastAccessedTime() { - return 0; - } - - @Override - public ServletContext getServletContext() { - return this.servletContext; - } - - @Override - public void setMaxInactiveInterval(int interval) { - - } - - @Override - public int getMaxInactiveInterval() { - return 0; - } - - @Override - public Object getAttribute(String name) { - return this.attributes.get(name); - } - - @Override - public Enumeration getAttributeNames() { - return Collections.enumeration(this.attributes.keySet()); - } - - @Override - public void setAttribute(String name, Object value) { - this.attributes.put(name, value); - } - - @Override - public void removeAttribute(String name) { - this.attributes.remove(name); - } - - @Override - public void invalidate() { - - } - - @Override - public boolean isNew() { - return false; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java deleted file mode 100644 index 397daaced..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.CountDownLatch; - -import jakarta.servlet.AsyncContext; -import jakarta.servlet.Filter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.FilterConfig; -import jakarta.servlet.RequestDispatcher; -import jakarta.servlet.Servlet; -import jakarta.servlet.ServletConfig; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRegistration; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.http.HttpStatus; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; -import org.springframework.web.context.ConfigurableWebApplicationContext; -import org.springframework.web.servlet.DispatcherServlet; - -/** - * Represents the main entry point into interaction with web application over light-weight proxy. - * After creating an instance via {@link #INSTANCE(Class...)} operation which will initialize the provided component - * classes of your web application (effectively starting your web application less web server), - * you use {@link #service(HttpServletRequest, HttpServletResponse)} operation to send request and receive a response. - * - * @author Oleg Zhurakousky - * - */ -public final class ProxyMvc { - - private static Log LOG = LogFactory.getLog(ProxyMvc.class); - - private final DispatcherServlet dispatcher; - - private final ConfigurableWebApplicationContext applicationContext; - - private ServletContext servletContext; - - public ConfigurableWebApplicationContext getApplicationContext() { - return this.applicationContext; - } - - public ServletContext getServletContext() { - return this.servletContext; - } - - public static ProxyMvc INSTANCE(ConfigurableWebApplicationContext applpicationContext) { - return new ProxyMvc(applpicationContext); - } - - public static ProxyMvc INSTANCE(Class... componentClasses) { - ConfigurableWebApplicationContext applpicationContext = ServerlessWebApplication.run(componentClasses, new String[] {}); - return INSTANCE(applpicationContext); - } - - - /** - * Private constructor, not for direct instantiation. - */ - ProxyMvc(ConfigurableWebApplicationContext applicationContext) { - this.applicationContext = applicationContext; - ProxyServletContext servletContext = new ProxyServletContext(); - this.applicationContext.setServletContext(servletContext); - this.applicationContext.refresh(); - - if (this.applicationContext.containsBean(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { - this.dispatcher = this.applicationContext.getBean(DispatcherServlet.class); - } - else { - this.dispatcher = new DispatcherServlet(this.applicationContext); - this.dispatcher.setDetectAllHandlerMappings(false); - ((GenericApplicationContext) this.applicationContext).registerBean(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME, - DispatcherServlet.class, () -> this.dispatcher); - } - - ServletRegistration.Dynamic reg = servletContext.addServlet(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME, dispatcher); - reg.setLoadOnStartup(1); - this.servletContext = applicationContext.getServletContext(); - try { - this.dispatcher.init(new ProxyServletConfig(this.servletContext)); - } - catch (Exception e) { - throw new IllegalStateException("Faild to create Spring MVC DispatcherServlet proxy", e); - } - } - - public void stop() { - this.applicationContext.stop(); - } - - /** - * Perform a request and return a type that allows chaining further actions, - * such as asserting expectations, on the result. - * - * @param requestBuilder used to prepare the request to execute; see static - * factory methods in - * {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders} - * @return an instance of {@link ResultActions} (never {@code null}) - * @see org.springframework.test.web.servlet.request.MockMvcRequestBuilders - * @see org.springframework.test.web.servlet.result.MockMvcResultMatchers - */ - public void service(HttpServletRequest request, HttpServletResponse response) throws Exception { - this.service(request, response, (CountDownLatch) null); - } - - - public void service(HttpServletRequest request, HttpServletResponse response, CountDownLatch latch) throws Exception { - ProxyFilterChain filterChain = new ProxyFilterChain(this.dispatcher); - filterChain.doFilter(request, response); - - AsyncContext asyncContext = request.getAsyncContext(); - if (asyncContext != null) { - filterChain = new ProxyFilterChain(this.dispatcher); - if (asyncContext instanceof ProxyAsyncContext proxyAsyncContext) { - proxyAsyncContext.addDispatchHandler(() -> { - try { - new ProxyFilterChain(this.dispatcher).doFilter(request, response); - asyncContext.complete(); - } - catch (Exception e) { - throw new IllegalStateException(e); - } - }); - } - } - - if (latch != null) { - latch.countDown(); - } - } - - private static class ProxyFilterChain implements FilterChain { - - @Nullable - private ServletRequest request; - - @Nullable - private ServletResponse response; - - private final List filters; - - @Nullable - private Iterator iterator; - - - /** - * Create a {@code FilterChain} with Filter's and a Servlet. - * - * @param servlet the {@link Servlet} to invoke in this {@link FilterChain} - * @param filters the {@link Filter}'s to invoke in this {@link FilterChain} - * @since 4.0.x - */ - ProxyFilterChain(DispatcherServlet servlet) { - List filters = new ArrayList<>(); - servlet.getServletContext().getFilterRegistrations().values().forEach(fr -> filters.add(((ProxyFilterRegistration) fr).getFilter())); - Assert.notNull(filters, "filters cannot be null"); - Assert.noNullElements(filters, "filters cannot contain null values"); - this.filters = initFilterList(servlet, filters.toArray(new Filter[] {})); - } - - private static List initFilterList(Servlet servlet, Filter... filters) { - Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet)); - return Arrays.asList(allFilters); - } - - /** - * Return the request that {@link #doFilter} has been called with. - */ - @Nullable - public ServletRequest getRequest() { - return this.request; - } - - /** - * Return the response that {@link #doFilter} has been called with. - */ - @Nullable - public ServletResponse getResponse() { - return this.response; - } - - /** - * Invoke registered {@link Filter Filters} and/or {@link Servlet} also saving - * the request and response. - */ - @Override - public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { - Assert.notNull(request, "Request must not be null"); - Assert.notNull(response, "Response must not be null"); - Assert.state(this.request == null, "This FilterChain has already been called!"); - - if (this.iterator == null) { - this.iterator = this.filters.iterator(); - } - - if (this.iterator.hasNext()) { - Filter nextFilter = this.iterator.next(); - nextFilter.doFilter(request, response, this); - } - - this.request = request; - this.response = response; - } - - /** - * A filter that simply delegates to a Servlet. - */ - private static final class ServletFilterProxy implements Filter { - - private final Servlet delegateServlet; - - private ServletFilterProxy(Servlet servlet) { - Assert.notNull(servlet, "servlet cannot be null"); - this.delegateServlet = servlet; - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - - try { - if (((HttpServletResponse) response).getStatus() != HttpStatus.OK.value() && request instanceof ProxyHttpServletRequest) { - ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, ((HttpServletResponse) response).getStatus()); - this.setErrorMessageAttribute((ProxyHttpServletRequest) request, (ProxyHttpServletResponse) response, null); - ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, ((HttpServletRequest) request).getRequestURI()); - - ((ProxyHttpServletRequest) request).setRequestURI("/error"); - this.delegateServlet.service(request, response); - } - else { - this.delegateServlet.service(request, response); - } - } - catch (Exception e) { - if (request instanceof ProxyHttpServletRequest) { - ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpStatus.INTERNAL_SERVER_ERROR.value()); - this.setErrorMessageAttribute((HttpServletRequest) request, (HttpServletResponse) response, e); - ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, e); - ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, ((HttpServletRequest) request).getRequestURI()); - ((ProxyHttpServletRequest) request).setRequestURI("/error"); - } - - LOG.error("Failed processing the request to: " + ((HttpServletRequest) request).getRequestURI(), e); - - this.delegateServlet.service(request, response); - } - } - - private void setErrorMessageAttribute(HttpServletRequest request, HttpServletResponse response, Exception exception) { - if (exception != null && StringUtils.hasText(exception.getMessage())) { - request.setAttribute(RequestDispatcher.ERROR_MESSAGE, exception.getMessage()); - } - else if (response instanceof ProxyHttpServletResponse proxyResponse && StringUtils.hasText(proxyResponse.getErrorMessage())) { - request.setAttribute(RequestDispatcher.ERROR_MESSAGE, proxyResponse.getErrorMessage()); - } - else { - request.setAttribute(RequestDispatcher.ERROR_MESSAGE, HttpStatus.valueOf(response.getStatus()).getReasonPhrase()); - - } - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - @Override - public void destroy() { - } - - @Override - public String toString() { - return this.delegateServlet.toString(); - } - } - } - - private static class ProxyServletConfig implements ServletConfig { - - private final ServletContext servletContext; - - ProxyServletConfig(ServletContext servletContext) { - this.servletContext = servletContext; - } - - @Override - public String getServletName() { - return DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME; - } - - @Override - public ServletContext getServletContext() { - return this.servletContext; - } - - @Override - public Enumeration getInitParameterNames() { - return Collections.enumeration(new ArrayList()); - } - - @Override - public String getInitParameter(String name) { - return null; - } - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java deleted file mode 100644 index 40fdbb1ca..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.EventListener; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - - -import jakarta.servlet.Filter; -import jakarta.servlet.FilterRegistration; -import jakarta.servlet.RequestDispatcher; -import jakarta.servlet.Servlet; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRegistration; -import jakarta.servlet.ServletRegistration.Dynamic; -import jakarta.servlet.SessionCookieConfig; -import jakarta.servlet.SessionTrackingMode; -import jakarta.servlet.descriptor.JspConfigDescriptor; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.util.ClassUtils; - -/** - * Empty no-op representation of {@link ServletContext} to satisfy required dependencies to - * successfully proxy incoming web requests to target web application. - * Most methods are not implemented. - * - * @author Oleg Zhurakousky - * - */ -public class ProxyServletContext implements ServletContext { - - private Log logger = LogFactory.getLog(ProxyServletContext.class); - - private static Enumeration EMPTY_ENUM = Collections.enumeration(new ArrayList()); - - @Override - public Enumeration getInitParameterNames() { - return EMPTY_ENUM; - } - - @Override - public Enumeration getAttributeNames() { - return EMPTY_ENUM; - } - - @Override - public String getContextPath() { - return ""; - } - - @Override - public ServletContext getContext(String uripath) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public int getMajorVersion() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public int getMinorVersion() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public int getEffectiveMajorVersion() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public int getEffectiveMinorVersion() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public String getMimeType(String file) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public Set getResourcePaths(String path) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public URL getResource(String path) throws MalformedURLException { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public InputStream getResourceAsStream(String path) { - return null; - } - - @Override - public RequestDispatcher getRequestDispatcher(String path) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public RequestDispatcher getNamedDispatcher(String name) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public void log(String msg) { - this.logger.info(msg); - } - - @Override - public void log(String message, Throwable throwable) { - this.logger.error(message, throwable); - } - - @Override - public String getRealPath(String path) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public String getServerInfo() { - return ClassUtils.isPresent("com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler", ClassUtils.getDefaultClassLoader()) - ? "aws-serverless-java-container/6.0" - : "serverless-web-proxy"; - } - - @Override - public String getInitParameter(String name) { - return null; - - } - - @Override - public boolean setInitParameter(String name, String value) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public Object getAttribute(String name) { - return null; - } - - @Override - public void setAttribute(String name, Object object) { - } - - @Override - public void removeAttribute(String name) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public String getServletContextName() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public Dynamic addServlet(String servletName, String className) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - Map registrations = new HashMap<>(); - - @Override - public Dynamic addServlet(String servletName, Servlet servlet) { - - ProxyServletRegistration registration = new ProxyServletRegistration(servletName, servlet, this); - this.registrations.put(servletName, registration); - return registration; - } - - @Override - public Dynamic addServlet(String servletName, Class servletClass) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public Dynamic addJspFile(String jspName, String jspFile) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public T createServlet(Class c) throws ServletException { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public ServletRegistration getServletRegistration(String servletName) { - return this.registrations.get(servletName); - } - - @Override - public Map getServletRegistrations() { - return this.registrations; - } - - @Override - public FilterRegistration.Dynamic addFilter(String filterName, String className) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { - ProxyFilterRegistration registration = new ProxyFilterRegistration(filterName, filter); - filterRegistrations.put(filterName, registration); - return registration; - } - - Map filterRegistrations = new HashMap<>(); - - @Override - public FilterRegistration.Dynamic addFilter(String filterName, Class filterClass) { - try { - Filter filter = filterClass.getDeclaredConstructor().newInstance(); - ProxyFilterRegistration registration = new ProxyFilterRegistration(filterName, filter); - filterRegistrations.put(filterName, registration); - return registration; - } - catch (Exception e) { - throw new IllegalStateException(e); - } - } - - @Override - public T createFilter(Class c) throws ServletException { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public FilterRegistration getFilterRegistration(String filterName) { - return this.filterRegistrations.get(filterName); - } - - @Override - public Map getFilterRegistrations() { - return this.filterRegistrations; - } - - @Override - public SessionCookieConfig getSessionCookieConfig() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public void setSessionTrackingModes(Set sessionTrackingModes) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public Set getDefaultSessionTrackingModes() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public Set getEffectiveSessionTrackingModes() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public void addListener(String className) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public void addListener(T t) { - // TODO Auto-generated method stub - - } - - @Override - public void addListener(Class listenerClass) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public T createListener(Class c) throws ServletException { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public JspConfigDescriptor getJspConfigDescriptor() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public ClassLoader getClassLoader() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public void declareRoles(String... roleNames) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public String getVirtualServerName() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public int getSessionTimeout() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public void setSessionTimeout(int sessionTimeout) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public String getRequestCharacterEncoding() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public void setRequestCharacterEncoding(String encoding) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public String getResponseCharacterEncoding() { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } - - @Override - public void setResponseCharacterEncoding(String encoding) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletRegistration.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletRegistration.java deleted file mode 100644 index 07474338e..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletRegistration.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.springframework.cloud.function.serverless.web; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -import jakarta.servlet.MultipartConfigElement; -import jakarta.servlet.Servlet; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletRegistration; -import jakarta.servlet.ServletSecurityElement; - -/** - * - * @author Oleg Zhurakousky - * @since 4.x - * - */ -public class ProxyServletRegistration implements ServletRegistration, ServletRegistration.Dynamic, Comparable { - - private final String servletName; - - private final Servlet servlet; - - private final ServletContext servletContext; - - private int loadOnStartup; - - public ProxyServletRegistration(String servletName, Servlet servlet, ServletContext servletContext) { - this.servlet = servlet; - this.servletName = servletName; - this.servletContext = servletContext; - } - - @Override - public String getName() { - return this.servletName; - } - - @Override - public String getClassName() { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean setInitParameter(String name, String value) { - // TODO Auto-generated method stub - return false; - } - - @Override - public String getInitParameter(String name) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Set setInitParameters(Map initParameters) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Map getInitParameters() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setAsyncSupported(boolean isAsyncSupported) { - // TODO Auto-generated method stub - - } - - @Override - public void setLoadOnStartup(int loadOnStartup) { - this.loadOnStartup = loadOnStartup; - } - - public int getLoadOnStartup() { - return this.loadOnStartup; - } - - @Override - public Set setServletSecurity(ServletSecurityElement constraint) { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setMultipartConfig(MultipartConfigElement multipartConfig) { - // TODO Auto-generated method stub - - } - - @Override - public void setRunAsRole(String roleName) { - // TODO Auto-generated method stub - - } - - @Override - public Set addMapping(String... urlPatterns) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getMappings() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getRunAsRole() { - // TODO Auto-generated method stub - return null; - } - - @Override - public int compareTo(ProxyServletRegistration o) { - return Integer.compare(this.loadOnStartup, o.getLoadOnStartup()); - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessWebApplication.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessWebApplication.java deleted file mode 100644 index 1c4bcd3ed..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessWebApplication.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.aot.AotDetector; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationContextFactory; -import org.springframework.boot.Banner; -import org.springframework.boot.BootstrapRegistryInitializer; -import org.springframework.boot.ConfigurableBootstrapContext; -import org.springframework.boot.DefaultApplicationArguments; -import org.springframework.boot.DefaultBootstrapContext; -import org.springframework.boot.DefaultPropertiesPropertySource; -import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor; -import org.springframework.boot.ResourceBanner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringApplicationRunListener; -import org.springframework.boot.SpringBootVersion; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.ansi.AnsiColor; -import org.springframework.boot.ansi.AnsiOutput; -import org.springframework.boot.ansi.AnsiStyle; -import org.springframework.boot.context.properties.source.ConfigurationPropertySources; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.aot.AotApplicationContextInitializer; -import org.springframework.core.Ordered; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.Environment; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; -import org.springframework.core.io.support.SpringFactoriesLoader; -import org.springframework.core.io.support.SpringFactoriesLoader.ArgumentResolver; -import org.springframework.core.metrics.ApplicationStartup; -import org.springframework.core.metrics.StartupStep; -import org.springframework.util.Assert; -import org.springframework.web.context.ConfigurableWebApplicationContext; - -/** - * - * @author Oleg Zhurakousky - * - */ -class ServerlessWebApplication extends SpringApplication { - - private static final Log logger = LogFactory.getLog(ServerlessWebApplication.class); - - private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT; - - private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT; - - private boolean allowCircularReferences; - - private boolean allowBeanDefinitionOverriding; - - private boolean logStartupInfo = true; - - private boolean lazyInitialization = false; - - private WebApplicationType webApplicationType; - - private List> initializers; - - public static ConfigurableWebApplicationContext run(Class[] primarySources, String[] args) { - return new ServerlessWebApplication(primarySources).run(args); - } - - ServerlessWebApplication(Class... classes) { - super(classes); - } - - @Override - public ConfigurableWebApplicationContext run(String... args) { - this.webApplicationType = WebApplicationType.SERVLET; - DefaultBootstrapContext bootstrapContext = createBootstrapContext(); - ConfigurableWebApplicationContext context = null; - SpringApplicationRunListeners listeners = getRunListeners(args); - listeners.starting(bootstrapContext, this.getMainApplicationClass()); - try { - ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); - ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); - Banner printedBanner = printBanner(environment); - context = (ConfigurableWebApplicationContext) createApplicationContext(); - context.setApplicationStartup(this.applicationStartup); - prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); - } - catch (Throwable ex) { - throw new IllegalStateException(ex); - } - - return context; - } - - - private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, - DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { - // Create and configure the environment - ConfigurableEnvironment environment = getOrCreateEnvironment(); - configureEnvironment(environment, applicationArguments.getSourceArgs()); - ConfigurationPropertySources.attach(environment); - listeners.environmentPrepared(bootstrapContext, environment); - DefaultPropertiesPropertySource.moveToEnd(environment); - Assert.state(!environment.containsProperty("spring.main.environment-prefix"), - "Environment prefix cannot be set via properties."); - bindToSpringApplication(environment); - ConfigurationPropertySources.attach(environment); - return environment; - } - - private ConfigurableEnvironment getOrCreateEnvironment() { - ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(this.webApplicationType); - if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) { - environment = ApplicationContextFactory.DEFAULT.createEnvironment(this.webApplicationType); - } - return environment; - } - - private SpringApplicationRunListeners getRunListeners(String[] args) { - ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this); - argumentResolver = argumentResolver.and(String[].class, args); - List listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class, argumentResolver); - return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup); - } - - private Banner printBanner(ConfigurableEnvironment environment) { - ResourceLoader resourceLoader = (this.getResourceLoader() != null) ? this.getResourceLoader() - : new DefaultResourceLoader(null); - SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, new SpringAwsBanner()); - return bannerPrinter.print(environment, this.getMainApplicationClass(), System.out); - } - - - private DefaultBootstrapContext createBootstrapContext() { - DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); - ArrayList bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); - bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); - return bootstrapContext; - } - - private List getSpringFactoriesInstances(Class type) { - return getSpringFactoriesInstances(type, null); - } - - private List getSpringFactoriesInstances(Class type, ArgumentResolver argumentResolver) { - return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, argumentResolver); - } - - private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, - ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, - ApplicationArguments applicationArguments, Banner printedBanner) { - context.setEnvironment(environment); - postProcessApplicationContext(context); - addAotGeneratedInitializerIfNecessary(this.initializers); - applyInitializers(context); - listeners.contextPrepared(context); - bootstrapContext.close(context); - if (this.logStartupInfo) { - logStartupInfo(context.getParent() == null); - logStartupProfileInfo(context); - } - // Add boot specific singleton beans - ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); - beanFactory.registerSingleton("springApplicationArguments", applicationArguments); - if (printedBanner != null) { - beanFactory.registerSingleton("springBootBanner", printedBanner); - } - if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) { - autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences); - if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) { - listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); - } - } - if (this.lazyInitialization) { - context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); - } - context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context)); - if (!AotDetector.useGeneratedArtifacts()) { - // Load the sources - Set sources = getAllSources(); - Assert.notEmpty(sources, "Sources must not be empty"); - load(context, sources.toArray(new Object[0])); - } - listeners.contextLoaded(context); - } - - private void addAotGeneratedInitializerIfNecessary(List> initializers) { - if (AotDetector.useGeneratedArtifacts()) { - List> aotInitializers = new ArrayList<>( - initializers.stream().filter(AotApplicationContextInitializer.class::isInstance).toList()); - if (aotInitializers.isEmpty()) { - String initializerClassName = this.getMainApplicationClass().getName() + "__ApplicationContextInitializer"; - aotInitializers.add(AotApplicationContextInitializer.forInitializerClasses(initializerClassName)); - } - initializers.removeAll(aotInitializers); - initializers.addAll(0, aotInitializers); - } - } - - private static class PropertySourceOrderingBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered { - - private final ConfigurableApplicationContext context; - - PropertySourceOrderingBeanFactoryPostProcessor(ConfigurableApplicationContext context) { - this.context = context; - } - - @Override - public int getOrder() { - return Ordered.HIGHEST_PRECEDENCE; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - DefaultPropertiesPropertySource.moveToEnd(this.context.getEnvironment()); - } - - } - - private static class SpringApplicationBannerPrinter { - - static final String BANNER_LOCATION_PROPERTY = "spring.banner.location"; - - static final String DEFAULT_BANNER_LOCATION = "banner.txt"; - - private static final Banner DEFAULT_BANNER = new SpringAwsBanner(); - - private final ResourceLoader resourceLoader; - - private final Banner fallbackBanner; - - SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) { - this.resourceLoader = resourceLoader; - this.fallbackBanner = fallbackBanner; - } - - Banner print(Environment environment, Class sourceClass, PrintStream out) { - Banner banner = getBanner(environment); - banner.printBanner(environment, sourceClass, out); - return new PrintedBanner(banner, sourceClass); - } - - private Banner getBanner(Environment environment) { - Banner textBanner = getTextBanner(environment); - if (textBanner != null) { - return textBanner; - } - if (this.fallbackBanner != null) { - return this.fallbackBanner; - } - return DEFAULT_BANNER; - } - - private Banner getTextBanner(Environment environment) { - String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION); - Resource resource = this.resourceLoader.getResource(location); - try { - if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) { - return new ResourceBanner(resource); - } - } - catch (IOException ex) { - // Ignore - } - return null; - } - - /** - * Decorator that allows a {@link Banner} to be printed again without needing to - * specify the source class. - */ - private static class PrintedBanner implements Banner { - - private final Banner banner; - - private final Class sourceClass; - - PrintedBanner(Banner banner, Class sourceClass) { - this.banner = banner; - this.sourceClass = sourceClass; - } - - @Override - public void printBanner(Environment environment, Class sourceClass, PrintStream out) { - sourceClass = (sourceClass != null) ? sourceClass : this.sourceClass; - this.banner.printBanner(environment, sourceClass, out); - } - - } - } - - private static class SpringAwsBanner implements Banner { - - private static final String[] BANNER = { "", "\n" - + " ____ _ _____ ______ _ _ _ \n" - + " / ___| _ __ _ __(_)_ __ __ _ / / \\ \\ / / ___| | | __ _ _ __ ___ | |__ __| | __ _ \n" - + " \\___ \\| '_ \\| '__| | '_ \\ / _` | / / _ \\ \\ /\\ / /\\___ \\ | | / _` | '_ ` _ \\| '_ \\ / _` |/ _` |\n" - + " ___) | |_) | | | | | | | (_| |/ / ___ \\ V V / ___) | | |__| (_| | | | | | | |_) | (_| | (_| |\n" - + " |____/| .__/|_| |_|_| |_|\\__, /_/_/ \\_\\_/\\_/ |____/ |_____\\__,_|_| |_| |_|_.__/ \\__,_|\\__,_|\n" - + " |_| |___/ \n" - + "" }; - - private static final String SPRING_BOOT = " :: Spring Boot :: "; - - private static final int STRAP_LINE_SIZE = 42; - - @Override - public void printBanner(Environment environment, Class sourceClass, PrintStream printStream) { - for (String line : BANNER) { - printStream.println(line); - } - String version = SpringBootVersion.getVersion(); - version = (version != null) ? " (v" + version + ")" : ""; - StringBuilder padding = new StringBuilder(); - while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) { - padding.append(" "); - } - - printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(), - AnsiStyle.FAINT, version)); - printStream.println(); - } - - } - - private static class SpringApplicationRunListeners { - - private final List listeners; - - private final ApplicationStartup applicationStartup; - - SpringApplicationRunListeners(Log log, List listeners, - ApplicationStartup applicationStartup) { - this.listeners = List.copyOf(listeners); - this.applicationStartup = applicationStartup; - } - - void starting(ConfigurableBootstrapContext bootstrapContext, Class mainApplicationClass) { - doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext), - (step) -> { - if (mainApplicationClass != null) { - step.tag("mainApplicationClass", mainApplicationClass.getName()); - } - }); - } - - void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { - doWithListeners("spring.boot.application.environment-prepared", - (listener) -> listener.environmentPrepared(bootstrapContext, environment)); - } - - void contextPrepared(ConfigurableApplicationContext context) { - doWithListeners("spring.boot.application.context-prepared", (listener) -> listener.contextPrepared(context)); - } - - void contextLoaded(ConfigurableApplicationContext context) { - doWithListeners("spring.boot.application.context-loaded", (listener) -> listener.contextLoaded(context)); - } - private void doWithListeners(String stepName, Consumer listenerAction) { - doWithListeners(stepName, listenerAction, null); - } - - private void doWithListeners(String stepName, Consumer listenerAction, - Consumer stepAction) { - StartupStep step = this.applicationStartup.start(stepName); - this.listeners.forEach(listenerAction); - if (stepAction != null) { - stepAction.accept(step); - } - step.end(); - } - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java deleted file mode 100644 index 9e0411eb1..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.serverless.web; - -import java.util.List; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.servlet.http.HttpServletRequest; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - - -import org.springframework.cloud.function.test.app.Pet; -import org.springframework.cloud.function.test.app.PetStoreSpringAppConfig; -import org.springframework.http.HttpStatus; -import org.springframework.security.test.context.support.WithMockUser; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class RequestResponseTests { - - private ObjectMapper mapper = new ObjectMapper(); - - private ProxyMvc mvc; - - @BeforeEach - public void before() { - this.mvc = ProxyMvc.INSTANCE(ProxyErrorController.class, PetStoreSpringAppConfig.class); - } - - @AfterEach - public void after() { - this.mvc.stop(); - } - - @Test - public void validateAccessDeniedWithCustomHandler() throws Exception { - HttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/foo"); - ProxyHttpServletResponse response = new ProxyHttpServletResponse(); - mvc.service(request, response); - assertThat(response.getErrorMessage()).isEqualTo("Can't touch this"); - assertThat(response.getStatus()).isEqualTo(403); - } - - @Test - public void validateGetListOfPojos() throws Exception { - HttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/pets"); - ProxyHttpServletResponse response = new ProxyHttpServletResponse(); - mvc.service(request, response); - TypeReference> tr = new TypeReference>() { - }; - List pets = mapper.readValue(response.getContentAsByteArray(), tr); - assertThat(pets.size()).isEqualTo(10); - assertThat(pets.get(0)).isInstanceOf(Pet.class); - } - - @Test - public void validateGetListOfPojosWithParam() throws Exception { - ProxyHttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/pets"); - request.setParameter("limit", "5"); - ProxyHttpServletResponse response = new ProxyHttpServletResponse(); - mvc.service(request, response); - TypeReference> tr = new TypeReference>() { - }; - List pets = mapper.readValue(response.getContentAsByteArray(), tr); - assertThat(pets.size()).isEqualTo(5); - assertThat(pets.get(0)).isInstanceOf(Pet.class); - } - - @WithMockUser("spring") - @Test - public void validateGetPojo() throws Exception { - HttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/pets/6e3cc370-892f-4efe-a9eb-82926ff8cc5b"); - ProxyHttpServletResponse response = new ProxyHttpServletResponse(); - mvc.service(request, response); - Pet pet = mapper.readValue(response.getContentAsByteArray(), Pet.class); - assertThat(pet).isNotNull(); - assertThat(pet.getName()).isNotEmpty(); - } - - @Test - public void errorThrownFromMethod() throws Exception { - HttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/pets/2"); - ProxyHttpServletResponse response = new ProxyHttpServletResponse(); - mvc.service(request, response); - assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value()); - assertThat(response.getErrorMessage()).isEqualTo("No such Dog"); - } - - @Test - public void errorUnexpectedWhitelabel() throws Exception { - HttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/pets/2/3/4"); - ProxyHttpServletResponse response = new ProxyHttpServletResponse(); - mvc.service(request, response); - assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value()); - } - - @Test - public void validatePostWithBody() throws Exception { - ProxyHttpServletRequest request = new ProxyHttpServletRequest(null, "POST", "/pets/"); - String jsonPet = "{\n" - + " \"id\":\"1234\",\n" - + " \"breed\":\"Canish\",\n" - + " \"name\":\"Foo\",\n" - + " \"date\":\"2012-04-23T18:25:43.511Z\"\n" - + "}"; - request.setContent(jsonPet.getBytes()); - request.setContentType("application/json"); - ProxyHttpServletResponse response = new ProxyHttpServletResponse(); - mvc.service(request, response); - Pet pet = mapper.readValue(response.getContentAsByteArray(), Pet.class); - assertThat(pet).isNotNull(); - assertThat(pet.getName()).isNotEmpty(); - } - - @Test - public void validatePostAsyncWithBody() throws Exception { - ProxyHttpServletRequest request = new ProxyHttpServletRequest(null, "POST", "/petsAsync/"); - String jsonPet = "{\n" - + " \"id\":\"1234\",\n" - + " \"breed\":\"Canish\",\n" - + " \"name\":\"Foo\",\n" - + " \"date\":\"2012-04-23T18:25:43.511Z\"\n" - + "}"; - request.setContent(jsonPet.getBytes()); - request.setContentType("application/json"); - ProxyHttpServletResponse response = new ProxyHttpServletResponse(); - mvc.service(request, response); - Pet pet = mapper.readValue(response.getContentAsByteArray(), Pet.class); - assertThat(pet).isNotNull(); - assertThat(pet.getName()).isNotEmpty(); - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/Pet.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/Pet.java deleted file mode 100644 index c0b8f0689..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/Pet.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test.app; - -import java.util.Date; - -public class Pet { - private String id; - private String breed; - private String name; - private Date dateOfBirth; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getBreed() { - return breed; - } - - public void setBreed(String breed) { - this.breed = breed; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Date getDateOfBirth() { - return dateOfBirth; - } - - public void setDateOfBirth(Date dateOfBirth) { - this.dateOfBirth = dateOfBirth; - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetData.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetData.java deleted file mode 100644 index 90b2a736f..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetData.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test.app; - - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -public class PetData { - private static List breeds = new ArrayList<>(); - static { - breeds.add("Afghan Hound"); - breeds.add("Beagle"); - breeds.add("Bernese Mountain Dog"); - breeds.add("Bloodhound"); - breeds.add("Dalmatian"); - breeds.add("Jack Russell Terrier"); - breeds.add("Norwegian Elkhound"); - } - - private static List names = new ArrayList<>(); - static { - names.add("Bailey"); - names.add("Bella"); - names.add("Max"); - names.add("Lucy"); - names.add("Charlie"); - names.add("Molly"); - names.add("Buddy"); - names.add("Daisy"); - names.add("Rocky"); - names.add("Maggie"); - names.add("Jake"); - names.add("Sophie"); - names.add("Jack"); - names.add("Sadie"); - names.add("Toby"); - names.add("Chloe"); - names.add("Cody"); - names.add("Bailey"); - names.add("Buster"); - names.add("Lola"); - names.add("Duke"); - names.add("Zoe"); - names.add("Cooper"); - names.add("Abby"); - names.add("Riley"); - names.add("Ginger"); - names.add("Harley"); - names.add("Roxy"); - names.add("Bear"); - names.add("Gracie"); - names.add("Tucker"); - names.add("Coco"); - names.add("Murphy"); - names.add("Sasha"); - names.add("Lucky"); - names.add("Lily"); - names.add("Oliver"); - names.add("Angel"); - names.add("Sam"); - names.add("Princess"); - names.add("Oscar"); - names.add("Emma"); - names.add("Teddy"); - names.add("Annie"); - names.add("Winston"); - names.add("Rosie"); - } - - public static List getBreeds() { - return breeds; - } - - public static List getNames() { - return names; - } - - public static String getRandomBreed() { - return breeds.get(ThreadLocalRandom.current().nextInt(0, breeds.size() - 1)); - } - - public static String getRandomName() { - return names.get(ThreadLocalRandom.current().nextInt(0, names.size() - 1)); - } - - public static Date getRandomDoB() { - GregorianCalendar gc = new GregorianCalendar(); - - int year = ThreadLocalRandom.current().nextInt(Calendar.getInstance().get(Calendar.YEAR) - 15, - Calendar.getInstance().get(Calendar.YEAR)); - - gc.set(Calendar.YEAR, year); - - int dayOfYear = ThreadLocalRandom.current().nextInt(1, gc.getActualMaximum(Calendar.DAY_OF_YEAR)); - - gc.set(Calendar.DAY_OF_YEAR, dayOfYear); - return gc.getTime(); - } -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java deleted file mode 100644 index 2c61478af..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test.app; - -import java.io.IOException; -import java.util.Collections; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.context.SecurityContextHolderFilter; -import org.springframework.web.filter.GenericFilterBean; -import org.springframework.web.servlet.HandlerAdapter; -import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - - -@Configuration -@Import({ PetsController.class }) -@EnableWebSecurity -public class PetStoreSpringAppConfig { - - /* - * Create required HandlerMapping, to avoid several default HandlerMapping - * instances being created - */ - @Bean - public HandlerMapping handlerMapping() { - return new RequestMappingHandlerMapping(); - } - - /* - * Create required HandlerAdapter, to avoid several default HandlerAdapter - * instances being created - */ - @Bean - public HandlerAdapter handlerAdapter() { - return new RequestMappingHandlerAdapter(); - } - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .addFilterBefore(new GenericFilterBean() { - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - SecurityContext securityContext = SecurityContextHolder.getContext(); - securityContext.setAuthentication(UsernamePasswordAuthenticationToken.authenticated("user", "password", - Collections.singleton(new SimpleGrantedAuthority("USER")))); - HttpSession session = ((HttpServletRequest) request).getSession(); - session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext); - chain.doFilter(request, response); - } - }, SecurityContextHolderFilter.class) - .securityMatcher("/foo") - .authorizeHttpRequests(authorize -> authorize - .anyRequest().hasRole("FOO") - ) - .exceptionHandling(f -> f.accessDeniedHandler(accessDeniedHandler())); - return http.build(); - } - - @Bean - public AccessDeniedHandler accessDeniedHandler() { - - return new AccessDeniedHandler() { - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, - AccessDeniedException accessDeniedException) throws IOException, ServletException { - response.sendError(403, "Can't touch this"); - } - }; - } - -} diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetsController.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetsController.java deleted file mode 100644 index 89a09ba14..000000000 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetsController.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test.app; - -import java.security.Principal; -import java.util.Optional; -import java.util.UUID; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.request.async.DeferredResult; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; - - -@RestController -@EnableWebMvc -public class PetsController { - - @RequestMapping(path = "/petsAsync/", method = RequestMethod.POST) - public DeferredResult createPetAsync(@RequestBody Pet newPet) { - if (newPet.getName() == null || newPet.getBreed() == null) { - return null; - } - - Pet dbPet = newPet; - dbPet.setId(UUID.randomUUID().toString()); - DeferredResult result = new DeferredResult(); - result.setResult(dbPet); - return result; - } - - @RequestMapping(path = "/pets/", method = RequestMethod.POST) - public Pet createPet(@RequestBody Pet newPet) { - if (newPet.getName() == null || newPet.getBreed() == null) { - return null; - } - - Pet dbPet = newPet; - dbPet.setId(UUID.randomUUID().toString()); - return dbPet; - } - - @RequestMapping(path = "/pets", method = RequestMethod.GET) - public Pet[] listPets(@RequestParam("limit") Optional limit, Principal principal) { - int queryLimit = 10; - if (limit.isPresent()) { - queryLimit = limit.get(); - } - - Pet[] outputPets = new Pet[queryLimit]; - - for (int i = 0; i < queryLimit; i++) { - Pet newPet = new Pet(); - newPet.setId(UUID.randomUUID().toString()); - newPet.setName(PetData.getRandomName()); - newPet.setBreed(PetData.getRandomBreed()); - newPet.setDateOfBirth(PetData.getRandomDoB()); - outputPets[i] = newPet; - } - - return outputPets; - } - - @RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET) - public Pet listPets(@PathVariable String petId) { - if (petId.equals("2")) { - throw new DogNotFoundException(); - } - Pet newPet = new Pet(); - newPet.setId(UUID.randomUUID().toString()); - newPet.setBreed(PetData.getRandomBreed()); - newPet.setDateOfBirth(PetData.getRandomDoB()); - newPet.setName(PetData.getRandomName()); - return newPet; - } - - @RequestMapping(path = "/foo", method = RequestMethod.GET) - public Pet foo() { - Pet newPet = new Pet(); - newPet.setId(UUID.randomUUID().toString()); - newPet.setBreed(PetData.getRandomBreed()); - newPet.setDateOfBirth(PetData.getRandomDoB()); - newPet.setName(PetData.getRandomName()); - return newPet; - } - - @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such Dog") // 404 - public class DogNotFoundException extends RuntimeException { - // ... - } -} diff --git a/spring-cloud-function-context/pom.xml b/spring-cloud-function-context/pom.xml deleted file mode 100644 index 12e023305..000000000 --- a/spring-cloud-function-context/pom.xml +++ /dev/null @@ -1,199 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-context - jar - spring-cloud-function-context - Implementation of core API for Spring Cloud Function - - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - - - 1.10.2 - - - - - net.jodah - typetools - 0.6.2 - - - org.springframework.boot - spring-boot-autoconfigure - - - org.springframework.cloud - spring-cloud-function-core - - - org.springframework - spring-messaging - - - org.springframework - spring-web - true - - - com.fasterxml.jackson.module - jackson-module-kotlin - true - - - org.springframework.boot - spring-boot-configuration-processor - true - - - com.google.code.gson - gson - true - - - com.fasterxml.jackson.core - jackson-databind - true - - - org.springframework.boot - spring-boot-starter-test - true - - - com.vaadin.external.google - android-json - - - - - io.projectreactor - reactor-test - test - - - - com.fasterxml.jackson.core - jackson-databind - - - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - true - - - org.jetbrains.kotlin - kotlin-reflect - true - - - org.jetbrains.kotlinx - kotlinx-coroutines-reactor - true - - - - - io.cloudevents - cloudevents-spring - 2.2.0 - true - - - - - org.springframework.boot - spring-boot-starter-actuator - true - - - - - io.micrometer - micrometer-observation - true - - - io.micrometer - micrometer-core - true - - - io.micrometer - micrometer-tracing - true - - - io.micrometer - micrometer-observation-test - test - - - io.micrometer - micrometer-tracing-bridge-otel - test - - - - - - - kotlin-maven-plugin - org.jetbrains.kotlin - - - compile - - compile - - - - ${project.basedir}/src/main/kotlin - ${project.basedir}/src/main/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - default-compile - none - - - - default-testCompile - none - - - java-compile - compile - - compile - - - - java-test-compile - test-compile - - testCompile - - - - - - - diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/actuator/FunctionsEndpoint.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/actuator/FunctionsEndpoint.java deleted file mode 100644 index 008a969db..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/actuator/FunctionsEndpoint.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.actuator; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; - -/** - * - * Actuator endpoint to access {@link FunctionCatalog}. - * - * @author Oleg Zhurakousky - * @since 3.2 - */ -@Endpoint(id = "functions") -public class FunctionsEndpoint { - - private final FunctionCatalog functionCatalog; - - public FunctionsEndpoint(FunctionCatalog functionCatalog) { - this.functionCatalog = functionCatalog; - } - - @ReadOperation - public Map> listAll() { - Map> allFunctions = new TreeMap<>(); - Set names = functionCatalog.getNames(null); - for (String name : names) { - FunctionInvocationWrapper function = functionCatalog.lookup(name); - Map functionMap = new LinkedHashMap<>(); - if (function.isFunction()) { - functionMap.put("type", "FUNCTION"); - functionMap.put("input-type", this.toSimplePolyIn(function)); - functionMap.put("output-type", this.toSimplePolyOut(function)); - } - else if (function.isConsumer()) { - functionMap.put("type", "CONSUMER"); - functionMap.put("input-type", this.toSimplePolyIn(function)); - } - else { - functionMap.put("type", "SUPPLIER"); - functionMap.put("output-type", this.toSimplePolyOut(function)); - } - allFunctions.put(name, functionMap); - } - - - return allFunctions; - } - - - private String toSimplePolyOut(FunctionInvocationWrapper function) { - return FunctionTypeUtils.getRawType(function.getItemType(function.getOutputType())).getSimpleName().toLowerCase(); - } - - private String toSimplePolyIn(FunctionInvocationWrapper function) { - return FunctionTypeUtils.getRawType(function.getItemType(function.getInputType())).getSimpleName().toLowerCase(); - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventHeaderEnricher.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventHeaderEnricher.java deleted file mode 100644 index 3e5b3fa8f..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventHeaderEnricher.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.cloudevent; - -/** - * Strategy that should be implemented by the user to help with outgoing Cloud Event - * headers.
- *
- * NOTE: The provided instance of {@link CloudEventMessageBuilder} may or may not be initialized - * with default values, so it is the responsibility of the user to ensure that all required Cloud Events - * attributes are set. That said, Spring frameworks which utilize this interface - * will ensure that the provided {@link CloudEventMessageBuilder} is initialized with default values, leaving - * you responsible to only set the attributes you need.
- * Once implemented, simply configure it as a bean and the framework will invoke it before - * the outbound Cloud Event Message is finalized. - * - *
- * @Bean
- * public CloudEventHeaderEnricher cloudEventHeaderEnricher() {
- *  return headers -> {
- *   return headers.setSource("https://interface21.com/").setType("com.interface21");
- *  };
- * }
- * 
- * - * @author Oleg Zhurakousky - * @author Dave Syer - * @since 3.1 - */ -@FunctionalInterface -public interface CloudEventHeaderEnricher { - - /** - * @param attributes instance of {@link CloudEventContext} - * @return modified {@link CloudEventContext} - */ - /** - * Will provide access to an open instance of {@link CloudEventMessageBuilder} so you - * can add additional attributes and headers. - * @param messageBuilder open instance of {@link CloudEventMessageBuilder} - * @return instance of {@link CloudEventMessageBuilder} - */ - CloudEventMessageBuilder enrich(CloudEventMessageBuilder messageBuilder); - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageBuilder.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageBuilder.java deleted file mode 100644 index f7f55ec45..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageBuilder.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.cloudevent; - -import java.net.URI; -import java.time.OffsetDateTime; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.function.context.message.MessageUtils; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.GenericMessage; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * Message builder which is aware of Cloud Event semantics. - * It provides type-safe setters for v1.0 Cloud Event attributes while - * supporting all other versions via convenient - * {@link #setHeader(String, Object)} method. - * - * @author Oleg Zhurakousky - * @since 3.1 - */ -public final class CloudEventMessageBuilder { - - protected Log logger = LogFactory.getLog(this.getClass()); - - private final Map headers; - - private T data; - - private CloudEventMessageBuilder(Map headers) { - this.headers = headers == null ? new HashMap<>() : headers; - } - - public static CloudEventMessageBuilder withData(T data) { - CloudEventMessageBuilder builder = new CloudEventMessageBuilder(null); - builder.data = data; - return builder; - } - - public static CloudEventMessageBuilder fromMessage(Message message) { - CloudEventMessageBuilder builder = new CloudEventMessageBuilder(new HashMap<>(message.getHeaders())); - builder.data = message.getPayload(); - return builder; - } - - public CloudEventMessageBuilder setId(String id) { - this.headers.put(CloudEventMessageUtils.ID, id); - return this; - } - - public CloudEventMessageBuilder setSource(URI uri) { - this.headers.put(CloudEventMessageUtils.SOURCE, uri); - return this; - } - - public CloudEventMessageBuilder setSource(String uri) { - this.headers.put(CloudEventMessageUtils.SOURCE, URI.create(uri)); - return this; - } - - public CloudEventMessageBuilder setSpecVersion(String specversion) { - this.headers.put(CloudEventMessageUtils.SPECVERSION, specversion); - return this; - } - - public CloudEventMessageBuilder setType(String type) { - this.headers.put(CloudEventMessageUtils.TYPE, type); - return this; - } - - public CloudEventMessageBuilder setDataContentType(String dataContentType) { - this.headers.put(CloudEventMessageUtils.DATACONTENTTYPE, dataContentType); - return this; - } - - public CloudEventMessageBuilder setDataSchema(URI dataSchema) { - this.headers.put(CloudEventMessageUtils.DATASCHEMA, dataSchema); - return this; - } - - public CloudEventMessageBuilder setDataSchema(String dataSchema) { - this.headers.put(CloudEventMessageUtils.DATASCHEMA, URI.create(dataSchema)); - return this; - } - - public CloudEventMessageBuilder setSubject(String subject) { - this.headers.put(CloudEventMessageUtils.SUBJECT, subject); - return this; - } - - public CloudEventMessageBuilder setTime(OffsetDateTime time) { - this.headers.put(CloudEventMessageUtils.TIME, time); - return this; - } - - public CloudEventMessageBuilder setTime(String time) { - this.headers.put(CloudEventMessageUtils.TIME, OffsetDateTime.parse(time)); - return this; - } - - public CloudEventMessageBuilder setHeader(String key, Object value) { - this.headers.put(key, value); - return this; - } - - public CloudEventMessageBuilder copyHeaders(Map headers) { - this.headers.putAll(headers); - return this; - } - - /** - * Returns a snapshot of the headers {@link Map} at the time this method is called. - * The returned Map is read-only. - * - * @return map of headers - */ - public Map toHeadersMap() { - return Collections.unmodifiableMap(this.headers); - } - - /** - * Will build the message ensuring that the Cloud Event attributes are all - * prefixed with the prefix determined by the framework. If you want to - * use a specific prefix please use {@link #build(String)} method. - * @return instance of {@link Message} - */ - public Message build() { - return this.doBuild(CloudEventMessageUtils.determinePrefixToUse(this.headers)); - } - - /** - * Will build the message ensuring that the Cloud Event attributes are - * prefixed with the 'attributePrefixToUse'. - * - * @param attributePrefixToUse prefix to use for attributes - * @return instance of {@link Message} - */ - public Message build(String attributePrefixToUse) { - Assert.isTrue(attributePrefixToUse.equals(CloudEventMessageUtils.DEFAULT_ATTR_PREFIX) - || attributePrefixToUse.equals(CloudEventMessageUtils.KAFKA_ATTR_PREFIX) - || attributePrefixToUse.equals(CloudEventMessageUtils.AMQP_ATTR_PREFIX), "Supported prefixes are " - + CloudEventMessageUtils.DEFAULT_ATTR_PREFIX - + ", " + CloudEventMessageUtils.KAFKA_ATTR_PREFIX - + ", " + CloudEventMessageUtils.AMQP_ATTR_PREFIX - + ". Was " + attributePrefixToUse); - if (StringUtils.hasText(attributePrefixToUse)) { - String[] keys = this.headers.keySet().toArray(new String[] {}); - for (String key : keys) { - if (key.startsWith(CloudEventMessageUtils.DEFAULT_ATTR_PREFIX)) { - this.swapPrefix(key, CloudEventMessageUtils.DEFAULT_ATTR_PREFIX, attributePrefixToUse); - } - else if (key.startsWith(CloudEventMessageUtils.AMQP_ATTR_PREFIX)) { - this.swapPrefix(key, CloudEventMessageUtils.AMQP_ATTR_PREFIX, attributePrefixToUse); - } - else if (key.startsWith(CloudEventMessageUtils.KAFKA_ATTR_PREFIX)) { - this.swapPrefix(key, CloudEventMessageUtils.KAFKA_ATTR_PREFIX, attributePrefixToUse); - } - } - } - return doBuild(attributePrefixToUse); - } - - private void swapPrefix(String key, String currentPrefix, String newPrefix) { - Object value = headers.remove(key); - key = key.substring(currentPrefix.length()); - this.headers.put(newPrefix + key, value); - } - - private Message doBuild(String prefix) { - if (!this.headers.containsKey(prefix + CloudEventMessageUtils._SPECVERSION)) { - this.headers.put(prefix + CloudEventMessageUtils._SPECVERSION, "1.0"); - } - if (!this.headers.containsKey(prefix + CloudEventMessageUtils._ID)) { - this.headers.put(prefix + CloudEventMessageUtils._ID, UUID.randomUUID().toString()); - } - this.headers.put(MessageUtils.MESSAGE_TYPE, CloudEventMessageUtils.CLOUDEVENT_VALUE); - - if (!this.headers.containsKey(prefix + CloudEventMessageUtils._TYPE)) { - this.headers.put(prefix + CloudEventMessageUtils._TYPE, this.data.getClass().getName()); - } - if (!this.headers.containsKey(prefix + CloudEventMessageUtils._SOURCE)) { - this.headers.put(prefix + CloudEventMessageUtils._SOURCE, URI.create("https://spring.io/")); - } - MessageHeaders headers = new MessageHeaders(this.headers); - GenericMessage message = new GenericMessage(this.data, headers); - Assert.isTrue(CloudEventMessageUtils.isCloudEvent(message), "The message does not appear to be a valid Cloud Event, " - + "since one of the required attributes (id, specversion, type, source) is missing"); - return message; - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtils.java deleted file mode 100644 index 47ad97e04..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtils.java +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.cloudevent; - -import java.net.URI; -import java.time.OffsetDateTime; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.springframework.cloud.function.context.message.MessageUtils; -import org.springframework.cloud.function.context.message.MessageUtils.MessageStructureWithCaseInsensitiveHeaderKeys; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.converter.ContentTypeResolver; -import org.springframework.messaging.converter.DefaultContentTypeResolver; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeType; -import org.springframework.util.MimeTypeUtils; -import org.springframework.util.StringUtils; - -/** - * Miscellaneous utility methods to assist with representing Cloud Event as Spring - * {@link Message}.
- * Primarily intended for the internal use within Spring-based frameworks and - * integrations. - * - * @author Oleg Zhurakousky - * @author Dave Syer - * @author Chris Bono - * @since 3.1 - */ -public final class CloudEventMessageUtils { - - private static final ContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver() { - - @Override - public MimeType resolve(@Nullable MessageHeaders headers) { - if (headers.containsKey("content-type")) { // this is temporary workaround for RSocket - return MimeType.valueOf(headers.get("content-type").toString()); - } - return super.resolve(headers); - } - - }; - - private CloudEventMessageUtils() { - } - - //=========== INTERNAL USE ONLY == - static String _DATA = "data"; - - static String _ID = "id"; - - static String _SOURCE = "source"; - - static String _SPECVERSION = "specversion"; - - static String _TYPE = "type"; - - static String _DATACONTENTTYPE = "datacontenttype"; - - static String _DATASCHEMA = "dataschema"; - - static String _SCHEMAURL = "schemaurl"; - - static String _SUBJECT = "subject"; - - static String _TIME = "time"; - // ================================ - - /** - * String value of 'cloudevent'. Typically used as {@link MessageUtils#MESSAGE_TYPE} - */ - public static String CLOUDEVENT_VALUE = "cloudevent"; - - /** - * String value of 'application/cloudevents' mime type. - */ - public static String APPLICATION_CLOUDEVENTS_VALUE = "application/cloudevents"; - - /** - * {@link MimeType} instance representing 'application/cloudevents' mime type. - */ - public static MimeType APPLICATION_CLOUDEVENTS = MimeTypeUtils.parseMimeType(APPLICATION_CLOUDEVENTS_VALUE); - - /** - * Prefix for attributes. - */ - public static String DEFAULT_ATTR_PREFIX = "ce-"; - - /** - * AMQP attributes prefix. - */ - public static String AMQP_ATTR_PREFIX = "cloudEvents:"; - - /** - * Prefix for attributes. - */ - public static String KAFKA_ATTR_PREFIX = "ce_"; - - /** - * Value for 'data' attribute. - */ - public static String DATA = DEFAULT_ATTR_PREFIX + _DATA; - - /** - * Value for 'id' attribute. - */ - public static String ID = DEFAULT_ATTR_PREFIX + _ID; - - /** - * Value for 'source' attribute. - */ - public static String SOURCE = DEFAULT_ATTR_PREFIX + _SOURCE; - - /** - * Value for 'specversion' attribute. - */ - public static String SPECVERSION = DEFAULT_ATTR_PREFIX + _SPECVERSION; - - /** - * Value for 'type' attribute. - */ - public static String TYPE = DEFAULT_ATTR_PREFIX + _TYPE; - - /** - * Value for 'datacontenttype' attribute. - */ - public static String DATACONTENTTYPE = DEFAULT_ATTR_PREFIX + _DATACONTENTTYPE; - - /** - * Value for 'dataschema' attribute. - */ - public static String DATASCHEMA = DEFAULT_ATTR_PREFIX + _DATASCHEMA; - - /** - * V03 name for 'dataschema' attribute. - */ - public static final String SCHEMAURL = DEFAULT_ATTR_PREFIX + _SCHEMAURL; - - /** - * Value for 'subject' attribute. - */ - public static String SUBJECT = DEFAULT_ATTR_PREFIX + _SUBJECT; - - /** - * Value for 'time' attribute. - */ - public static String TIME = DEFAULT_ATTR_PREFIX + _TIME; - - - public static String getId(Message message) { - String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + MessageHeaders.ID); - } - - public static URI getSource(Message message) { - String prefix = determinePrefixToUse(message.getHeaders()); - return safeGetURI(message.getHeaders(), prefix + _SOURCE); - } - - public static String getSpecVersion(Message message) { - String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + _SPECVERSION); - } - - public static String getType(Message message) { - String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + _TYPE); - } - - public static String getDataContentType(Message message) { - String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + _DATACONTENTTYPE); - } - - public static URI getDataSchema(Message message) { - String prefix = determinePrefixToUse(message.getHeaders()); - return safeGetURI(message.getHeaders(), prefix + _DATASCHEMA); - } - - public static String getSubject(Message message) { - String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + _SUBJECT); - } - - public static OffsetDateTime getTime(Message message) { - String prefix = determinePrefixToUse(message.getHeaders()); - return (OffsetDateTime) message.getHeaders().get(prefix + _TIME); - } - - @SuppressWarnings("unchecked") - public static T getData(Message message) { - return (T) message.getPayload(); - } - - public static Map getAttributes(Message message) { - return message.getHeaders().entrySet().stream() - .filter(e -> isAttribute(e.getKey())) - .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); - } - - /** - * This method does several things. - * First it canonicalizes Cloud Events attributes ensuring that they are all prefixed - * with 'ce-' prefix regardless where they came from. - * It also transforms structured-mode Cloud Event to binary-mode and then canonicalizes attributes - * as well as described in the previous sentence. - */ - @SuppressWarnings("unchecked") - static Message toCanonical(Message inputMessage, MessageConverter messageConverter) { - inputMessage = canonicalizeHeadersWithPossibleCopy(inputMessage); - Map headers = new HashMap<>(inputMessage.getHeaders()); - - boolean isCloudEvent = isCloudEvent(inputMessage); - if (isCloudEvent && headers.containsKey("content-type")) { - inputMessage = MessageBuilder.fromMessage(inputMessage).setHeader(MessageHeaders.CONTENT_TYPE, headers.get("content-type")).build(); - } - MimeType contentType = contentTypeResolver.resolve(inputMessage.getHeaders()); - String inputContentType = (String) inputMessage.getHeaders().get(DATACONTENTTYPE); - // first check the obvious and see if content-type is `cloudevents` - if (!isCloudEvent && contentType != null) { - // structured-mode - - if (contentType.getType().equals(APPLICATION_CLOUDEVENTS.getType()) && contentType - .getSubtype().startsWith(APPLICATION_CLOUDEVENTS.getSubtype())) { - - String dataContentType = StringUtils.hasText(inputContentType) ? inputContentType - : MimeTypeUtils.APPLICATION_JSON_VALUE; - - String suffix = contentType.getSubtypeSuffix() == null ? "json" : contentType.getSubtypeSuffix(); - MimeType cloudEventDeserializationContentType = MimeTypeUtils - .parseMimeType(contentType.getType() + "/" + suffix); - Message cloudEventMessage = MessageBuilder.fromMessage(inputMessage) - .setHeader(MessageHeaders.CONTENT_TYPE, cloudEventDeserializationContentType) - .setHeader(DATACONTENTTYPE, dataContentType).build(); - Map structuredCloudEvent = (Map) messageConverter - .fromMessage(cloudEventMessage, Map.class); - canonicalizeHeaders(structuredCloudEvent, true); - return buildBinaryMessageFromStructuredMap(structuredCloudEvent, - inputMessage.getHeaders()); - } - } - else if (StringUtils.hasText(inputContentType)) { - // binary-mode, but DATACONTENTTYPE was specified explicitly so we set it as CT to ensure proper message converters are used. - return MessageBuilder.fromMessage(inputMessage).setHeader(MessageHeaders.CONTENT_TYPE, inputContentType) - .build(); - } - return inputMessage; - } - - /** - * Attempts to {@link #canonicalizeHeaders canonicalize} the headers of a message. - * @param message the message - * @return a copy of the message with the canonicalized headers or the passed in unmodified message if no - * headers were canonicalized - */ - // VisibleForTesting - static Message canonicalizeHeadersWithPossibleCopy(Message message) { - Map headers = new HashMap<>(message.getHeaders()); - boolean headersModified = canonicalizeHeaders(headers, false); - if (headersModified) { - message = MessageBuilder.fromMessage(message) - .removeHeaders("*") - .copyHeaders(headers) - .build(); - } - return message; - } - - /** - * Will canonicalize Cloud Event attributes (headers) by ensuring canonical - * prefix for all attributes and extensions regardless of where they came from. - * The canonical prefix is 'ce-'. - * - * So, for example 'ce_source' will become 'ce-source'. - * @param headers message headers - * @param structured boolean signifying that headers map represents structured Cloud Event - * at which point attributes without any prefix will still be treated as - * Cloud Event attributes. - * @return whether the headers were modified during the process - */ - private static boolean canonicalizeHeaders(Map headers, boolean structured) { - boolean modified = false; - String[] keys = headers.keySet().toArray(new String[] {}); - for (String key : keys) { - if (key.startsWith(DEFAULT_ATTR_PREFIX)) { - Object value = headers.remove(key); - String newKey = DEFAULT_ATTR_PREFIX + key.substring(DEFAULT_ATTR_PREFIX.length()); - headers.put(newKey, value); - modified |= (!Objects.equals(key, newKey)); - } - else if (key.startsWith(KAFKA_ATTR_PREFIX)) { - Object value = headers.remove(key); - key = key.substring(KAFKA_ATTR_PREFIX.length()); - headers.put(DEFAULT_ATTR_PREFIX + key, value); - modified = true; - } - else if (key.startsWith(AMQP_ATTR_PREFIX)) { - Object value = headers.remove(key); - key = key.substring(AMQP_ATTR_PREFIX.length()); - headers.put(DEFAULT_ATTR_PREFIX + key, value); - modified = true; - } - else if (structured) { - Object value = headers.remove(key); - headers.put(DEFAULT_ATTR_PREFIX + key, value); - modified = true; - } - } - return modified; - } - - /** - * Determines attribute prefix based on the presence of certain well defined headers. - * @param messageHeaders map of message headers - * @return prefix (e.g., 'ce_' or 'ce-' etc.) - */ - static String determinePrefixToUse(Map messageHeaders) { - return determinePrefixToUse(messageHeaders, false); - } - - static String determinePrefixToUse(Map messageHeaders, boolean strict) { - String targetProtocol = (String) messageHeaders.get(MessageUtils.TARGET_PROTOCOL); - String prefix = determinePrefixToUse(targetProtocol); - if (StringUtils.hasText(prefix) && (strict || StringUtils.hasText((String) messageHeaders.get(prefix + _SPECVERSION)))) { - return prefix; - } - else { - for (String key : messageHeaders.keySet()) { - if (key.startsWith(DEFAULT_ATTR_PREFIX)) { - return DEFAULT_ATTR_PREFIX; - } - else if (key.startsWith(KAFKA_ATTR_PREFIX)) { - return KAFKA_ATTR_PREFIX; - } - else if (key.startsWith(AMQP_ATTR_PREFIX)) { - return AMQP_ATTR_PREFIX; - } - } - } - - return DEFAULT_ATTR_PREFIX; - } - - /** - * Determines attribute prefix based on the provided target protocol. - * @param targetProtocol target protocol (see {@link MessageUtils#TARGET_PROTOCOL} - * @return prefix (e.g., 'ce_' or 'ce-' etc.) - */ - static String determinePrefixToUse(String targetProtocol) { - if (StringUtils.hasText(targetProtocol)) { - if (Protocols.KAFKA.equals(targetProtocol)) { - return CloudEventMessageUtils.KAFKA_ATTR_PREFIX; - } - else if (Protocols.AMQP.equals(targetProtocol)) { - return CloudEventMessageUtils.AMQP_ATTR_PREFIX; - } - else if (Protocols.HTTP.equals(targetProtocol)) { - return CloudEventMessageUtils.DEFAULT_ATTR_PREFIX; - } - } - return ""; - } - - /** - * Will check for the existence of required attributes. Assumes attributes (headers) - * are in canonical form. - * @param message input {@link Message} - * @return true if this Message represents Cloud Event in binary-mode - */ - public static boolean isCloudEvent(Message message) { - MessageStructureWithCaseInsensitiveHeaderKeys _message = MessageUtils.toCaseInsensitiveHeadersStructure(message); - return (_message.getHeaders().containsKey(SPECVERSION) - && _message.getHeaders().containsKey(TYPE) - && _message.getHeaders().containsKey(SOURCE)) - || - (_message.getHeaders().containsKey(_SPECVERSION) - && _message.getHeaders().containsKey(_TYPE) - && _message.getHeaders().containsKey(_SOURCE)) - || - (_message.getHeaders().containsKey(AMQP_ATTR_PREFIX + _SPECVERSION) - && _message.getHeaders().containsKey(AMQP_ATTR_PREFIX + _TYPE) - && _message.getHeaders().containsKey(AMQP_ATTR_PREFIX + _SOURCE)) - || - (_message.getHeaders().containsKey(KAFKA_ATTR_PREFIX + _SPECVERSION) - && _message.getHeaders().containsKey(KAFKA_ATTR_PREFIX + _TYPE) - && _message.getHeaders().containsKey(KAFKA_ATTR_PREFIX + _SOURCE)); - } - - private static boolean isAttribute(String key) { - return key.startsWith(DEFAULT_ATTR_PREFIX) || key.startsWith(AMQP_ATTR_PREFIX) || key.startsWith(KAFKA_ATTR_PREFIX); - } - - private static Message buildBinaryMessageFromStructuredMap(Map structuredCloudEvent, - MessageHeaders originalHeaders) { - Object payload = structuredCloudEvent.remove(DATA); - if (payload == null) { - payload = Collections.emptyMap(); - } - - CloudEventMessageBuilder messageBuilder = CloudEventMessageBuilder - .withData(payload) - .copyHeaders(structuredCloudEvent); - - for (String key : originalHeaders.keySet()) { - if (!MessageHeaders.ID.equals(key)) { - messageBuilder.setHeader(key, originalHeaders.get(key)); - } - } - - return messageBuilder.build(); - } - - private static URI safeGetURI(Map map, String key) { - Object uri = map.get(key); - if (uri != null && uri instanceof String) { - uri = URI.create((String) uri); - } - return (URI) uri; - } - - public static class Protocols { - static String AMQP = "amqp"; - static String AVRO = "avro"; - static String HTTP = "http"; - static String JSON = "json"; - static String KAFKA = "kafka"; - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionExtensionConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionExtensionConfiguration.java deleted file mode 100644 index 89a424c63..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionExtensionConfiguration.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.cloudevent; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.cloud.function.core.FunctionInvocationHelper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; - -/** - * Configuration class with components relevant to Cloud Events support. - * - * @author Oleg Zhurakousky - * @since 3.1 - */ -@Configuration(proxyBeanMethods = false) -public class CloudEventsFunctionExtensionConfiguration { - - // The following two beans are intended to be mutually exclusive. Only one should be activated based - // on the presence of Cloud Event SDK API - @Bean - @ConditionalOnMissingBean - public FunctionInvocationHelper> nativeFunctionInvocationHelper(@Nullable CloudEventHeaderEnricher cloudEventHeadersProvider) { - return new CloudEventsFunctionInvocationHelper(cloudEventHeadersProvider); - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionInvocationHelper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionInvocationHelper.java deleted file mode 100644 index 9d1b6fa2b..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventsFunctionInvocationHelper.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.cloudevent; - -import java.net.URI; -import java.util.UUID; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.BeansException; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.message.MessageUtils; -import org.springframework.cloud.function.core.FunctionInvocationHelper; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.converter.CompositeMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * Implementation of {@link FunctionInvocationHelper} to support Cloud Events. - * This is a primary (and the only) integration bridge with {@link FunctionInvocationWrapper}. - * - * @author Oleg Zhurakousky - * @since 3.1 - * - */ -public class CloudEventsFunctionInvocationHelper implements FunctionInvocationHelper>, ApplicationContextAware { - - private Log logger = LogFactory.getLog(this.getClass()); - - private ConfigurableApplicationContext applicationContext; - - private final CloudEventHeaderEnricher cloudEventAttributesProvider; - - private CompositeMessageConverter messageConverter; - - private final Class CLOUD_EVENT_CLASS; - - CloudEventsFunctionInvocationHelper(@Nullable CloudEventHeaderEnricher cloudEventHeadersProvider) { - this.cloudEventAttributesProvider = cloudEventHeadersProvider; - Class clazz = null; - try { - clazz = Thread.currentThread().getContextClassLoader().loadClass("io.cloudevents.CloudEvent"); - } - catch (Exception e) { - // ignore - } - CLOUD_EVENT_CLASS = clazz; - } - - @Override - public boolean isRetainOuputAsMessage(Message message) { - return message.getHeaders().containsKey(MessageUtils.TARGET_PROTOCOL) || (message.getHeaders().containsKey(MessageUtils.MESSAGE_TYPE) - && message.getHeaders().get(MessageUtils.MESSAGE_TYPE).equals(CloudEventMessageUtils.CLOUDEVENT_VALUE)); - } - - @Override - public Message preProcessInput(Message input, Object inputConverter) { - // TODO find a way to invoke it conditionally. May be check for certain headers with all known prefixes as well as content type - try { - return CloudEventMessageUtils.toCanonical(input, (MessageConverter) inputConverter); - } - catch (Exception e) { - return input; - } - } - - public void setMessageConverter(CompositeMessageConverter messageConverter) { - this.messageConverter = messageConverter; - } - - @Override - public Message postProcessResult(Object result, Message input) { - Object convertedResult = result; - if (this.messageConverter != null && CLOUD_EVENT_CLASS != null && CLOUD_EVENT_CLASS.isAssignableFrom(result.getClass())) { - convertedResult = this.messageConverter.toMessage(result, input.getHeaders()); - } - - String targetPrefix = CloudEventMessageUtils.DEFAULT_ATTR_PREFIX; - if (input != null) { - targetPrefix = CloudEventMessageUtils.determinePrefixToUse(input.getHeaders(), true); - } - else if (result instanceof Message) { - targetPrefix = CloudEventMessageUtils.determinePrefixToUse(((Message) result).getHeaders(), true); - } - - Assert.hasText(targetPrefix, "Unable to determine prefix for Cloud Event atttributes, " - + "which they must have according to protocol specification. Consider adding 'target-protocol' " - + "header with values of one of the supported protocols - [kafka, amqp, http]"); - if (logger.isDebugEnabled()) { - logger.debug("Cloud event attributes will be prefixed with '" + targetPrefix + "'"); - } - return this.doPostProcessResult(convertedResult, targetPrefix); - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = (ConfigurableApplicationContext) applicationContext; - } - - private Message doPostProcessResult(Object result, String targetPrefix) { - Message resultMessage = null; //result instanceof Message ? (Message) result : null; - CloudEventMessageBuilder messageBuilder; - if (result instanceof Message) { - if (CloudEventMessageUtils.isCloudEvent((Message) result)) { - messageBuilder = CloudEventMessageBuilder.fromMessage((Message) result); - } - else { - return (Message) result; - } - } - else { - messageBuilder = CloudEventMessageBuilder - .withData(result) - .setId(UUID.randomUUID().toString()) - .setSource(URI.create("http://spring.io/" + getApplicationName())) - .setType(result.getClass().getName()); - } - - if (this.cloudEventAttributesProvider != null) { - messageBuilder = this.cloudEventAttributesProvider.enrich(messageBuilder); - } - - resultMessage = messageBuilder.build(targetPrefix); - if (logger.isDebugEnabled()) { - logger.debug("Cloud Event result message: " + resultMessage); - } - return resultMessage; - } - - private String getApplicationName() { - ConfigurableEnvironment environment = this.applicationContext.getEnvironment(); - String name = environment.getProperty("spring.application.name"); - return (StringUtils.hasText(name) ? name : ""); - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/DefaultMessageRoutingHandler.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/DefaultMessageRoutingHandler.java deleted file mode 100644 index 87c1b7ec7..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/DefaultMessageRoutingHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.util.function.Consumer; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.messaging.Message; - -/** - * Strategy for implementing a handler for un-routable messages. - * Works in parallel with {@link RoutingFunction}. When registered as a bean, RoutingFunction will not throw - * an exception if it can not route message and instead such message will be routed to this function. - * Its default implementation simply logs the un-routable event. - * Users are encouraged to provide their own implementation of this class. - * - * @author Oleg Zhurakousky - * @since 3.2.9 - * - */ -public class DefaultMessageRoutingHandler implements Consumer> { - - Log logger = LogFactory.getLog(DefaultMessageRoutingHandler.class); - - @Override - public void accept(Message message) { - if (logger.isDebugEnabled()) { - logger.debug("Route-to function can not be located in FunctionCatalog. Dropping unroutable message: " + message + ""); - } - else { - logger.warn("Route-to function can not be located in FunctionCatalog. Droping message"); - } - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionCatalog.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionCatalog.java deleted file mode 100644 index b8a84a8ef..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionCatalog.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public interface FunctionCatalog { - - /** - * Will look up the instance of the functional interface by name only. - * - * @param functionDefinition the definition of the functional interface. Must - * not be null; - * @return instance of the functional interface registered with this catalog - */ - default T lookup(String functionDefinition) { - return this.lookup(null, functionDefinition, (String[]) null); - } - - /** - * Will look up the instance of the functional interface by name and type which - * can only be Supplier, Consumer or Function. If type is not provided, the - * lookup will be made based on name only. - * - * @param instance type - * @param type the type of functional interface. Can be null - * @param functionDefinition the definition of the functional interface. Must - * not be null; - * @return instance of the functional interface registered with this catalog - */ - default T lookup(Class type, String functionDefinition) { - return this.lookup(type, functionDefinition, (String[]) null); - } - - - /** - * Will look up the instance of the functional interface by name only. - * This lookup method assumes a very specific semantics which are: function sub-type(s) - * expected to be {@code Message}.
- * For example, - *

- * {@code Function, Message>} or - *
- * {@code Function>, Flux>>} or - *
- * {@code Consumer>>} etc. . . - *

- * The {@code acceptedOutputMimeTypes} are the string representation of {@link MimeType} where each - * mime-type in the provided array would correspond to the output with the same index - * (for cases of functions with multiple outputs) and is used to convert such output back - * to {@code Message}. - * If you need to provide several accepted types per specific output you can simply delimit - * them with comma (e.g., {@code application/json,text/plain...}). - * - * @param instance type which should be one of {@link Supplier}, {@link Function} or {@link Consumer}. - * @param functionDefinition the definition of a function (e.g., 'foo' or 'foo|bar') - * @param acceptedOutputMimeTypes acceptedOutputMimeTypes array of string representation of {@link MimeType}s - * used to convert function output back to {@code Message}. - * @return instance of the functional interface registered with this catalog - */ - default T lookup(String functionDefinition, String... expectedOutputMimeTypes) { - return this.lookup(null, functionDefinition, expectedOutputMimeTypes); - } - - T lookup(Class type, String functionDefinition, String... expectedOutputMimeTypes); - - Set getNames(Class type); - - /** - * Return the count of functions registered in this catalog. - * - * @return the count of functions registered in this catalog - */ - default int size() { - throw new UnsupportedOperationException("This instance of FunctionCatalog does not support this operation"); - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionProperties.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionProperties.java deleted file mode 100644 index efad20fb4..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionProperties.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.beans.BeansException; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; -import org.springframework.util.CollectionUtils; - -/** - * - * @author Oleg Zhurakousky - * @since 3.0 - * - */ -@ConfigurationProperties(prefix = FunctionProperties.PREFIX) -public class FunctionProperties implements EnvironmentAware, ApplicationContextAware { - - /** - * The name prefix for properties defined by this properties class. - */ - public final static String PREFIX = "spring.cloud.function"; - - /** - * Name of the header to be used to instruct function to apply this content type for output conversion. - */ - public final static String EXPECT_CONTENT_TYPE_HEADER = "expected-content-type"; - - /** - * SpEL expression to be used with RoutingFunction. - */ - public final static String ROUTING_EXPRESSION = PREFIX + ".routing-expression"; - - /** - * The name of function definition property. - */ - public final static String FUNCTION_DEFINITION = PREFIX + ".definition"; - - /** - * Definition of the function to be used. This could be function name (e.g., 'myFunction') - * or function composition definition (e.g., 'myFunction|yourFunction') - */ - private String definition; - - /** - * SpEL expression which should result in function definition (e.g., function name or composition instruction). - * NOTE: SpEL evaluation context's root object is the input argument (e.g., Message). - */ - private String routingExpression; - - /** - * List of functions that are not eligible to be registered in Function Catalog. - */ - private final List ineligibleDefinitions; - - private Map configuration; - - private String expectedContentType; - - private Environment environment; - - private ApplicationContext applicationContext; - - public FunctionProperties() { - ineligibleDefinitions = new ArrayList<>(); - String[] definitions = new String[] { - "org.springframework.boot", - "org.springframework.cloud.function.cloudevent.CloudEventsFunctionExtensionConfiguration", - "org.springframework.cloud.function.context.config.FunctionsEndpointAutoConfiguration", - "classLoaderMetrics", - "jvmMemoryMetrics", - "jvmInfoMetrics", - "jvmCompilationMetrics", - "uptimeMetrics", - "kotlinToFunctionTransformer", - "CloudEventsMessageConverterConfiguration" - }; - ineligibleDefinitions.addAll(Arrays.asList(definitions)); - } - - public Map getConfiguration() { - return configuration; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void setConfiguration(Map configuration) { - for (Entry entry : configuration.entrySet()) { - String propertyX = "spring.cloud.function.configuration." + entry.getKey() + ".input-header-mapping-expression."; - String propertyY = "spring.cloud.function.configuration." + entry.getKey() + ".inputHeaderMappingExpression."; - Map headerMapping = entry.getValue().getInputHeaderMappingExpression(); - if (!CollectionUtils.isEmpty(headerMapping)) { - for (Object k : headerMapping.keySet()) { - if (this.environment.containsProperty(propertyX + k) || this.environment.containsProperty(propertyY + k)) { - Map current = entry.getValue().getInputHeaderMappingExpression(); - if (current.containsKey("0")) { - ((Map) current.get("0")).put(k, headerMapping.get(k)); - } - else { - entry.getValue().setInputHeaderMappingExpression(Collections.singletonMap("0", current)); - break; - } - } - } - } - propertyX = "spring.cloud.function.configuration." + entry.getKey() + ".output-header-mapping-expression."; - propertyY = "spring.cloud.function.configuration." + entry.getKey() + ".outputHeaderMappingExpression."; - headerMapping = entry.getValue().getOutputHeaderMappingExpression(); - if (!CollectionUtils.isEmpty(headerMapping)) { - for (Object k : headerMapping.keySet()) { - if (this.environment.containsProperty(propertyX + k) || this.environment.containsProperty(propertyY + k)) { - Map current = entry.getValue().getOutputHeaderMappingExpression(); - if (current.containsKey("0")) { - ((Map) current.get("0")).put(k, headerMapping.get(k)); - } - else { - entry.getValue().setOutputHeaderMappingExpression(Collections.singletonMap("0", current)); - break; - } - } - } - } - } - - this.configuration = configuration; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - this.applicationContext = applicationContext; - } - - public ApplicationContext getApplicationContext() { - return this.applicationContext; - } - - public String getDefinition() { - return definition; - } - - public void setDefinition(String definition) { - this.definition = definition; - } - - public String getRoutingExpression() { - return routingExpression; - } - - public void setRoutingExpression(String routingExpression) { - this.routingExpression = routingExpression; - } - - public String getExpectedContentType() { - return this.expectedContentType; - } - - public void setExpectedContentType(String expectedContentType) { - this.expectedContentType = expectedContentType; - } - - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; - } - - public List getIneligibleDefinitions() { - return new ArrayList<>(this.ineligibleDefinitions); - } - - public void setIneligibleDefinitions(List definitions) { - this.ineligibleDefinitions.addAll(definitions); - } - - public static class FunctionConfigurationProperties { - - private Map inputHeaderMappingExpression; - - private Map outputHeaderMappingExpression; - - private boolean copyInputHeaders; - - public Map getInputHeaderMappingExpression() { - return inputHeaderMappingExpression; - } - - public void setInputHeaderMappingExpression(Map inputHeaderMappingExpression) { - this.inputHeaderMappingExpression = inputHeaderMappingExpression; - } - - public Map getOutputHeaderMappingExpression() { - return outputHeaderMappingExpression; - } - - public void setOutputHeaderMappingExpression( - Map outputHeaderMappingExpression) { - this.outputHeaderMappingExpression = outputHeaderMappingExpression; - } - - public boolean isCopyInputHeaders() { - return copyInputHeaders; - } - - public void setCopyInputHeaders(boolean copyInputHeaders) { - this.copyInputHeaders = copyInputHeaders; - } - - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java deleted file mode 100644 index 6e3b3666b..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2016-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.config.KotlinLambdaToFunctionAutoConfiguration; -import org.springframework.core.KotlinDetector; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; - -/** - * @param target type - * @author Dave Syer - * @author Oleg Zhurakousky - * @author Soby Chacko - */ -public class FunctionRegistration implements BeanNameAware { - - /** - * Suffix used to add to the name of FunctionRegistration bean that - * corresponds to the an actual function bean. It is often used when - * the actual function bean may not be a java Function (e.g., Kotlin) - * and certain custom wrapping is required. - *
- * NOTE: This is not intended as oublis API - */ - public static String REGISTRATION_NAME_SUFFIX = "_registration"; - - private final Set names = new LinkedHashSet<>(); - - private final Map properties = new LinkedHashMap<>(); - - private T target; - - private Type type; - - /** - * In certain cased, {@link FunctionRegistration} needs to know details - * about the user function. For example, when the user provides a BiConsumer, - * we wrap that in a regular Function. There are actions that a downstream client - * needs to take based on the type information of the wrapped function such as - * not creating an output binding when the wrapped type is a BiConsumer. - */ - private Object userFunction; - - /** - * Creates instance of FunctionRegistration. - * @param target instance of {@link Supplier}, {@link Function} or {@link Consumer} - * @param names additional set of names for this registration. Additional names can be - * provided {@link #name(String)} or {@link #names(String...)} operations. - */ - public FunctionRegistration(T target, String... names) { - Assert.notNull(target, "'target' must not be null"); - this.target = target; - this.names(names); - } - - public Map getProperties() { - return this.properties; - } - - public Set getNames() { - return this.names; - } - - /** - * Will set the names for this registration clearing all previous names first. If you - * want to add a name or set or names to the existing set of names use - * {@link #names(Collection)} or {@link #name(String)} or {@link #names(String...)} - * operations. - * @param names - bean names - */ - public void setNames(Set names) { - this.names.clear(); - this.names.addAll(names); - } - - public Type getType() { - return this.type; - } - - public T getTarget() { - return this.target; - } - - public FunctionRegistration properties(Map properties) { - this.properties.putAll(properties); - return this; - } - - public FunctionRegistration type(Type type) { - this.type = type; - if (KotlinDetector.isKotlinPresent() && this.target instanceof KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper) { - return this; - } - Type discoveredFunctionType = FunctionTypeUtils.discoverFunctionTypeFromClass(this.target.getClass()); - if (discoveredFunctionType == null) { // only valid for Kafka Stream KStream[] return type. - return null; - } - - Class inputType = FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(discoveredFunctionType)); - Class outputType = FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(discoveredFunctionType)); - - if (inputType != null && inputType != Object.class && outputType != null && outputType != Object.class) { - Assert.isTrue((inputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) - && outputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type)))), - "Discovered function type does not match provided function type. Discovered: " - + discoveredFunctionType + "; Provided: " + type); - } - else if (inputType == null && outputType != Object.class) { - Assert.isTrue(outputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))), - "Discovered function type does not match provided function type. Discovered: " - + discoveredFunctionType + "; Provided: " + type); - } - else if (outputType == null && inputType != Object.class) { - Assert.isTrue(inputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))), - "Discovered function type does not match provided function type. Discovered: " - + discoveredFunctionType + "; Provided: " + type); - } - - - return this; - } - - /** - * Allows to override the target of this registration with a new target that typically - * wraps the original target. This typically happens when original target is wrapped - * into its {@link Flux} counterpart (e.g., Function into FluxFunction) - * @param target new target - * @return this registration with new target - */ - public FunctionRegistration target(T target) { - this.target = target; - return this; - } - - public FunctionRegistration name(String name) { - return this.names(name); - } - - public FunctionRegistration names(Collection names) { - this.names.addAll(names); - return this; - } - - public FunctionRegistration names(String... names) { - return this.names(Arrays.asList(names)); - } - - /** - * Transforms (wraps) function identified by the 'target' to its {@code Flux} - * equivalent unless it already is. For example, {@code Function} - * becomes {@code Function, Flux>} - * @param the expected target type of the function (e.g., FluxFunction) - * @return {@code FunctionRegistration} with the appropriately wrapped target. - * - */ - - @Override - public void setBeanName(String name) { - if (CollectionUtils.isEmpty(this.names)) { - this.name(name); - } - } - - public Object getUserFunction() { - return userFunction; - } - - public void setUserFunction(Object userFunction) { - this.userFunction = userFunction; - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistry.java deleted file mode 100644 index 6770a8196..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistry.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -/** - * @author Dave Syer - * - */ -public interface FunctionRegistry extends FunctionCatalog { - - void register(FunctionRegistration registration); - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionTypeProcessor.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionTypeProcessor.java deleted file mode 100644 index 67349fc9a..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionTypeProcessor.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2019-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.HashSet; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.springframework.aot.generate.GenerationContext; -import org.springframework.aot.hint.MemberCategory; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; -import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; -import org.springframework.beans.factory.aot.BeanFactoryInitializationCode; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.config.FunctionContextUtils; -import org.springframework.cloud.function.context.message.MessageUtils; - -/** - * Ensure that Function/Consumer input types are reflectively available. - * - * @author Oleg Zhurakousky - */ -public class FunctionTypeProcessor implements BeanFactoryInitializationAotProcessor { - - @Override - public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { - Set> typeHints = new HashSet<>(); - - String[] names = beanFactory.getBeanDefinitionNames(); - for (int i = 0; i < names.length; i++) { - String beanName = names[i]; - Class beanClass = beanFactory.getType(beanName); - if (this.isFunction(beanClass)) { - Type functionType = FunctionTypeUtils.discoverFunctionTypeFromClass(beanClass); - - if (!(functionType instanceof ParameterizedType)) { - functionType = FunctionContextUtils.findType(beanFactory, beanName); - } - this.registerAllGenericTypes((ParameterizedType) functionType, typeHints); - } - } - return new ReflectiveProcessorBeanFactoryInitializationAotContribution(typeHints.toArray(Class[]::new)); - } - - private void registerAllGenericTypes(ParameterizedType type, Set> typeHints) { - Type[] types = type.getActualTypeArguments(); - for (int i = 0; i < types.length; i++) { - Type functionParameterType = types[i]; - String name = functionParameterType.getTypeName(); - if (!isCoreJavaType(name)) { - typeHints.add(FunctionTypeUtils.getRawType(functionParameterType)); - } - if (functionParameterType instanceof ParameterizedType) { - this.registerAllGenericTypes((ParameterizedType) functionParameterType, typeHints); - } - } - } - - private boolean isCoreJavaType(String className) { - return className.startsWith("java.") || className.startsWith("javax."); - } - - private boolean isFunction(Class beanType) { - return Function.class.isAssignableFrom(beanType) - || Consumer.class.isAssignableFrom(beanType) - || Supplier.class.isAssignableFrom(beanType); - } - - private static final class ReflectiveProcessorBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution { - - private final Class[] typeHints; - - private ReflectiveProcessorBeanFactoryInitializationAotContribution(Class[] typeHints) { - this.typeHints = typeHints; - } - - @Override - public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) { - RuntimeHints runtimeHints = generationContext.getRuntimeHints(); - for (int i = 0; i < typeHints.length; i++) { - runtimeHints.reflection().registerType(typeHints[i], MemberCategory.PUBLIC_FIELDS, - MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); - } - - // known static types - runtimeHints.reflection().registerType(MessageUtils.MessageStructureWithCaseInsensitiveHeaderKeys.class, - MemberCategory.INVOKE_PUBLIC_METHODS); - } - - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java deleted file mode 100644 index 9c01b78df..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2017-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.boot.ApplicationContextFactory; -import org.springframework.boot.WebApplicationType; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; - -import static java.util.Arrays.stream; - -/** - * @author Dave Syer - * @author Semyon Fishman - * @author Oleg Zhurakousky - */ -public class FunctionalSpringApplication - extends org.springframework.boot.SpringApplication { - - /** - * Flag to say that context is functional beans. - */ - public static final String SPRING_FUNCTIONAL_ENABLED = "spring.functional.enabled"; - - /** - * Enumeration of web application types. - */ - public static final String SPRING_WEB_APPLICATION_TYPE = "spring.main.web-application-type"; - - /** - * Name of default property source. - */ - private static final String DEFAULT_PROPERTIES = "defaultProperties"; - - public FunctionalSpringApplication(Class... primarySources) { - super(primarySources); - setApplicationContextFactory(ApplicationContextFactory.ofContextClass(GenericApplicationContext.class)); - if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", - null)) { - setWebApplicationType(WebApplicationType.REACTIVE); - } - else { - setWebApplicationType(WebApplicationType.NONE); - } - } - - public static void main(String[] args) throws Exception { - FunctionalSpringApplication.run(new Class[0], args); - } - - public static ConfigurableApplicationContext run(Class primarySource, - String... args) { - return run(new Class[] { primarySource }, args); - } - - public static ConfigurableApplicationContext run(Class[] primarySources, - String[] args) { - return new FunctionalSpringApplication(primarySources).run(args); - } - - @SuppressWarnings("unchecked") - @Override - protected void postProcessApplicationContext(ConfigurableApplicationContext context) { - super.postProcessApplicationContext(context); - boolean functional = false; - Assert.isInstanceOf(GenericApplicationContext.class, context, - "ApplicationContext must be an instanceof GenericApplicationContext"); - for (Object source : getAllSources()) { - System.out.println("======> SOURCE: " + source); - Class type = null; - Object handler = null; - if (source instanceof String) { - String name = (String) source; - if (ClassUtils.isPresent(name, null)) { - type = ClassUtils.resolveClassName(name, null); - } - } - else if (source instanceof Class) { - type = (Class) source; - } - else { - type = source.getClass(); - handler = source; - } - if (ApplicationContextInitializer.class.isAssignableFrom(type)) { - if (handler == null) { - handler = BeanUtils.instantiateClass(type); - } - - ApplicationContextInitializer initializer = - (ApplicationContextInitializer) handler; - initializer.initialize(context); - functional = true; - } - else if (Function.class.isAssignableFrom(type) - || Consumer.class.isAssignableFrom(type) - || Supplier.class.isAssignableFrom(type)) { - Class functionType = type; - Object function = handler; - if (source.equals(functionType)) { - context.addBeanFactoryPostProcessor(beanFactory -> { - BeanDefinitionRegistry bdRegistry = (BeanDefinitionRegistry) beanFactory; - if (!ObjectUtils.isEmpty(context.getBeanNamesForType(functionType))) { - stream(context.getBeanNamesForType(functionType)) - .forEach(beanName -> bdRegistry.registerAlias(beanName, "function")); - } - else { - this.register((GenericApplicationContext) context, function, functionType); - } - }); - } - else { - this.register((GenericApplicationContext) context, function, functionType); - } - functional = true; - } - } - if (functional) { - defaultProperties(context); - } - } - - private void register(GenericApplicationContext context, Object function, Class functionType) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>( - handler(context, function, functionType)) - .type(functionType)); - } - - private Object handler(GenericApplicationContext generic, Object handler, - Class type) { - if (handler == null) { - handler = generic.getAutowireCapableBeanFactory().createBean(type); - } - return handler; - } - - @Override - protected void load(ApplicationContext context, Object[] sources) { - if (!context.getEnvironment().getProperty(SPRING_FUNCTIONAL_ENABLED, - Boolean.class, false)) { - super.load(context, sources); - } - } - - private void defaultProperties(ConfigurableApplicationContext context) { - MutablePropertySources sources = context.getEnvironment().getPropertySources(); - if (!sources.contains(DEFAULT_PROPERTIES)) { - sources.addLast( - new MapPropertySource(DEFAULT_PROPERTIES, Collections.emptyMap())); - } - @SuppressWarnings("unchecked") - Map source = (Map) sources.get(DEFAULT_PROPERTIES) - .getSource(); - Map map = new HashMap<>(source); - map.put(SPRING_FUNCTIONAL_ENABLED, "true"); - map.put(SPRING_WEB_APPLICATION_TYPE, getWebApplicationType()); - sources.replace(DEFAULT_PROPERTIES, - new MapPropertySource(DEFAULT_PROPERTIES, map)); - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/MessageRoutingCallback.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/MessageRoutingCallback.java deleted file mode 100644 index 2ba6000f3..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/MessageRoutingCallback.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.messaging.Message; - -/** - * Java-based strategy to assist with determining the name of the route-to function definition. - * Once implementation is registered as a bean in application context - * it will be picked up by the {@link RoutingFunction}. - * - * While {@link RoutingFunction} provides several mechanisms to determine the route-to function definition - * this callback takes precedence over all of them. - * - * @author Oleg Zhurakousky - * @since 3.1 - */ -public interface MessageRoutingCallback { - - /** - * Computes and returns the instance of {@link FunctionRoutingResult} which encapsulates, - * at the very minimum, function definition. - *

- * Providing such message is primarily an optimization feature. It could be useful for cases - * where routing procedure is complex and results in, let's say, conversion of the payload to - * the target type, which would effectively be thrown away if the ability to modify the target - * message for downstream use didn't exist, resulting in repeated transformation, type conversion etc. - * - * @param message input message - * @return instance of {@link FunctionRoutingResult} containing the result of the routing computation - */ - default String routingResult(Message message) { - return (String) message.getHeaders().get(FunctionProperties.FUNCTION_DEFINITION); - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PollableBean.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PollableBean.java deleted file mode 100644 index 5994cd7f4..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PollableBean.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.function.Supplier; - -import org.springframework.context.annotation.Bean; - -/** - * - * A marker and qualifier annotation to signal that - * annotated functional factory method is a bean (e.g., Supplier, Function or Consumer) - * that also needs to be polled periodically. - *
- * This has special significance to the reactive suppliers (e.g., {@code Supplier>}), - * since by default they are treated as producers of an infinite stream. - * However if such suppliers produce a finite stream they may need to be triggered again. - *
- *
- * NOTE: The spring-cloud-function framework provides no default post processing behavior for this annotation. This - * means that annotating a factory method with this annotation will not have any effect without some application/framework - * specific post processing (see spring-cloud-stream as an example). - * - * - * @author Oleg Zhurakousky - * @since 3.0 - * - */ -@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Bean -@Documented -public @interface PollableBean { - - /** - * Signals to the post processors of this annotation that the result produced by the - * annotated {@link Supplier} has to be split. Specifics on how to split and what - * to split are left to the underlying framework. - * - * @return true if the resulting stream produced by the - * annotated {@link Supplier} has to be split. - */ - boolean splittable() default true; -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PostProcessingFunction.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PostProcessingFunction.java deleted file mode 100644 index 5c5d1d23c..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/PostProcessingFunction.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.util.function.Function; - -import org.springframework.messaging.Message; - -/** - * Strategy for implementing function with post processing behavior. - *
- * The core framework only provides support for the post-processing behavior. - * The actual invocation of post-processing is left to the end user or the framework which - * integrates Spring Cloud Function. This is because post-processing can mean different things - * in different execution contexts. See {@link #postProcess(Message)} method for more information. - * - * @param - input type - * @param - output type - * - * @author Oleg Zhurakousky - * @since 4.0.3 - * - */ -public interface PostProcessingFunction extends Function { - - @SuppressWarnings("unchecked") - @Override - default O apply(I t) { - return (O) t; - } - - /** - * Will post process the result of this's function invocation after this function has been triggered. - *
- * This operation is not managed/invoked by the core functionality of the Spring Cloud Function. - * It is specifically designed as a hook for other frameworks and extensions to invoke after - * this function was "triggered" and there is a requirement to do some post processing. The word "triggered" - * can mean different things in different execution contexts. For example, in spring-cloud-stream it means - * that the function has been invoked and the result of the function has been sent to the target destination. - * - * The boolean value argument - 'success' - allows the triggering framework to signal success or - * failure of its triggering operation whatever that may mean. - * - * @param result - the result of function invocation as an instance of {@link Message} including all the metadata as message headers. - */ - default void postProcess(Message result) { - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java deleted file mode 100644 index 31fa6e41f..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright 2019-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.Collection; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; - -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.config.FunctionContextUtils; -import org.springframework.cloud.function.context.config.KotlinLambdaToFunctionAutoConfiguration; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.cloud.function.core.FunctionInvocationHelper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.KotlinDetector; -import org.springframework.core.ResolvableType; -import org.springframework.core.convert.ConversionService; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.converter.CompositeMessageConverter; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * Implementation of {@link FunctionRegistry} capable of discovering functions in {@link BeanFactory}. - * - * @author Oleg Zhurakousky - * @author Soby Chacko - */ -public class BeanFactoryAwareFunctionRegistry extends SimpleFunctionRegistry implements ApplicationContextAware { - - private GenericApplicationContext applicationContext; - - public BeanFactoryAwareFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, - JsonMapper jsonMapper, @Nullable FunctionProperties functionProperties, @Nullable FunctionInvocationHelper> functionInvocationHelper) { - super(conversionService, messageConverter, jsonMapper, functionProperties, functionInvocationHelper); - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = (GenericApplicationContext) applicationContext; - } - - /* - * Basically gives an approximation only including function registrations and SFC. - * Excludes possible POJOs that can be treated as functions - */ - @Override - public int size() { - return this.applicationContext.getBeanNamesForType(Supplier.class).length + - this.applicationContext.getBeanNamesForType(Function.class).length + - this.applicationContext.getBeanNamesForType(Consumer.class).length + - super.size(); - } - - /* - * Doesn't account for POJO so we really don't know until it's been lookedup - */ - @Override - public Set getNames(Class type) { - Set registeredNames = super.getNames(type); - if (type == null) { - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(Function.class))); - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(Supplier.class))); - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(Consumer.class))); - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(BiFunction.class))); - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(BiConsumer.class))); - registeredNames - .addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(FunctionRegistration.class))); - } - else { - registeredNames.addAll(Arrays.asList(this.applicationContext.getBeanNamesForType(type))); - } - return registeredNames; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public T lookup(Class type, String functionDefinition, String... expectedOutputMimeTypes) { - functionDefinition = StringUtils.hasText(functionDefinition) - ? functionDefinition - : this.applicationContext.getEnvironment().getProperty(FunctionProperties.FUNCTION_DEFINITION, ""); - if (!this.applicationContext.containsBean(functionDefinition) || !KotlinDetector.isKotlinType(this.applicationContext.getBean(functionDefinition).getClass())) { - functionDefinition = this.normalizeFunctionDefinition(functionDefinition); - } - if (!StringUtils.hasText(functionDefinition)) { - Collection functionalBeans = this.getNames(null).stream() - .filter(name -> !RoutingFunction.FUNCTION_NAME.equals(name)) - .filter(name -> !RoutingFunction.DEFAULT_ROUTE_HANDLER.equals(name)) - .collect(Collectors.toList()); - if (!CollectionUtils.isEmpty(functionalBeans) && functionalBeans.size() > 1) { - logger.warn("Multiple functional beans were found " + functionalBeans + ", thus can't determine default function definition. Please " - + "use 'spring.cloud.function.definition' property to explicitly define it. "); - } - } - if (!isFunctionDefinitionEligible(functionDefinition)) { - return null; - } - FunctionInvocationWrapper function = this.doLookup(type, functionDefinition, expectedOutputMimeTypes); - Object syncInstance = functionDefinition == null ? this : functionDefinition; - synchronized (syncInstance) { - if (function == null) { - Set functionRegistratioinNames = super.getNames(null); - String[] functionNames = StringUtils.delimitedListToStringArray(functionDefinition.replaceAll(",", "|").trim(), "|"); - for (String functionName : functionNames) { - if (functionRegistratioinNames.contains(functionName) && logger.isDebugEnabled()) { - logger.debug("Skipping function '" + functionName + "' since it is already present"); - } - else { - Object functionCandidate = this.discoverFunctionInBeanFactory(functionName); - if (functionCandidate != null) { - Type functionType = null; - FunctionRegistration functionRegistration = null; - if (functionCandidate instanceof FunctionRegistration) { - functionRegistration = (FunctionRegistration) functionCandidate; - } - else if (functionCandidate instanceof BiFunction || functionCandidate instanceof BiConsumer) { - functionRegistration = this.registerMessagingBiFunction(functionCandidate, functionName); - } - else if (KotlinDetector.isKotlinType(functionCandidate.getClass())) { - KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper wrapper = - new KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper(functionCandidate); - wrapper.setName(functionName); - wrapper.setBeanFactory(this.applicationContext.getBeanFactory()); - functionRegistration = wrapper.getFunctionRegistration(); - } - else if (this.isFunctionPojo(functionCandidate, functionName)) { - Method functionalMethod = FunctionTypeUtils.discoverFunctionalMethod(functionCandidate.getClass()); - functionCandidate = this.proxyTarget(functionCandidate, functionalMethod); - functionType = FunctionTypeUtils.fromFunctionMethod(functionalMethod); - } - else if (this.isSpecialFunctionRegistration(functionNames, functionName)) { - functionRegistration = this.applicationContext - .getBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, FunctionRegistration.class); - } - else { - functionType = FunctionTypeUtils.discoverFunctionType(functionCandidate, functionName, this.applicationContext); - } - if (functionRegistration == null) { - functionRegistration = new FunctionRegistration(functionCandidate, functionName).type(functionType); - } - // Certain Kafka Streams functions such as KStream[] return types could be null (esp when using Kotlin). - this.register(functionRegistration); - } - else { - if (logger.isDebugEnabled()) { - logger.debug("Function '" + functionName + "' is not available in FunctionCatalog or BeanFactory"); - } - } - } - } - function = super.doLookup(type, functionDefinition, expectedOutputMimeTypes); - } - } - - return (T) function; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private FunctionRegistration registerMessagingBiFunction(Object userFunction, String functionName) { - Type biFunctionType = FunctionContextUtils.findType(this.applicationContext.getBeanFactory(), functionName); - Type inputType1 = Object.class; - Type inputType2 = Object.class; - if (biFunctionType instanceof ParameterizedType) { - inputType1 = ((ParameterizedType) biFunctionType).getActualTypeArguments()[0]; - inputType2 = ((ParameterizedType) biFunctionType).getActualTypeArguments()[1]; - } - - if (!FunctionTypeUtils.isTypeMap(inputType2)) { - logger.debug("BiFunction's second argument must be assignable to Map, since BiFunction " - + "represents parsed Message with first argument being payload and second headers. " - + "Other signatures are not supported at the moment."); - } - - ResolvableType messageType = ResolvableType.forClassWithGenerics(Message.class, ResolvableType.forType(inputType1)); - Type biFunctionWrapperType = ResolvableType.forClassWithGenerics(Function.class, messageType, ResolvableType.forType(inputType2)).getType(); - - Function wrapperFunction = message -> { - Object payload = ((Message) message).getPayload(); - if (payload.getClass().getName().equals("org.springframework.kafka.support.KafkaNull")) { - payload = null; - } - if (userFunction instanceof BiConsumer) { - ((BiConsumer) userFunction).accept(payload, ((Message) message).getHeaders()); - return null; - } - else { - return ((BiFunction) userFunction).apply(payload, ((Message) message).getHeaders()); - } - }; - - FunctionRegistration functionRegistration = new FunctionRegistration<>(wrapperFunction, functionName).type(biFunctionWrapperType); - functionRegistration.setUserFunction(userFunction); - return functionRegistration; - } - - private Object discoverFunctionInBeanFactory(String functionName) { - Object functionCandidate = null; - if (this.applicationContext.containsBean(functionName)) { - functionCandidate = this.applicationContext.getBean(functionName); - } - else { - try { - functionCandidate = BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.applicationContext.getBeanFactory(), Object.class, functionName); - } - catch (Exception e) { - // ignore since there is no safe isAvailable-kind of method - } - } - return functionCandidate; - } - - @Override - protected boolean containsFunction(String functionName) { - return super.containsFunction(functionName) || this.applicationContext.containsBean(functionName); - } - - private boolean isFunctionPojo(Object functionCandidate, String functionName) { - return !functionCandidate.getClass().isSynthetic() - && !(functionCandidate instanceof Supplier) - && !(functionCandidate instanceof Function) - && !(functionCandidate instanceof Consumer) - && !this.applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX); - } - - /** - * At the moment 'special function registration' simply implies that a bean under the provided functionName - * may have already been wrapped and registered as FunuctionRegistration with BeanFactory under the name of - * the function suffixed with {@link FunctionRegistration#REGISTRATION_NAME_SUFFIX} - * (e.g., 'myKotlinFunction_registration'). - *

- * At the moment only Kotlin module does this - * - * @param functionCandidate candidate for FunctionInvocationWrapper instance - * @param functionName the name of the function - * @return true if this function candidate qualifies - */ - private boolean isSpecialFunctionRegistration(Object functionCandidate, String functionName) { - return this.applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX); - } - - private Object proxyTarget(Object targetFunction, Method actualMethodToCall) { - ProxyFactory pf = new ProxyFactory(targetFunction); - pf.setProxyTargetClass(true); - pf.setInterfaces(Function.class); - pf.addAdvice(new MethodInterceptor() { - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - return actualMethodToCall.invoke(invocation.getThis(), invocation.getArguments()); - } - }); - return pf.getProxy(); - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionAroundWrapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionAroundWrapper.java deleted file mode 100644 index d656420d9..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionAroundWrapper.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; - -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.messaging.Message; -import org.springframework.util.StringUtils; - -/** - * Wrapper that acts as around advise over function invocation. - * If registered as bean it will be autowired into {@link FunctionInvocationWrapper}. - * Keep in mind that it only affects imperative invocations where input is {@link Message} - * - * NOTE: This API is experimental and and could change without notice. It is - * intended for internal use only (e.g., spring-cloud-sleuth) - * - * @author Oleg Zhurakousky - * @since 3.1 - */ -public abstract class FunctionAroundWrapper { - - private static final Log log = LogFactory.getLog(FunctionAroundWrapper.class); - - public final Object apply(Object input, FunctionInvocationWrapper targetFunction) { - String functionalTracingEnabledStr = System.getProperty("spring.cloud.function.observability.enabled"); - boolean functionalTracingEnabled = !StringUtils.hasText(functionalTracingEnabledStr) - || Boolean.parseBoolean(functionalTracingEnabledStr); - if (functionalTracingEnabled && !(input instanceof Publisher) && input instanceof Message && !FunctionTypeUtils.isCollectionOfMessage(targetFunction.getOutputType())) { - return this.doApply(input, targetFunction); - } - else { - return targetFunction.apply(input); - } - } - - protected abstract Object doApply(Object input, FunctionInvocationWrapper targetFunction); -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionCatalogEvent.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionCatalogEvent.java deleted file mode 100644 index 8ac2bfefb..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionCatalogEvent.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import org.springframework.context.ApplicationEvent; - -/** - * @author Dave Syer - * - */ -@SuppressWarnings("serial") -public class FunctionCatalogEvent extends ApplicationEvent { - - public FunctionCatalogEvent(Object source) { - super(source); - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionRegistrationEvent.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionRegistrationEvent.java deleted file mode 100644 index 63b18af2f..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionRegistrationEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author Dave Syer - * - */ -@SuppressWarnings("serial") -public class FunctionRegistrationEvent extends FunctionCatalogEvent { - - private final Class type; - - private final Set names; - - public FunctionRegistrationEvent(Object source, Class type, Set names) { - super(source); - this.type = type; - this.names = new HashSet<>(names); - } - - public Class getType() { - return this.type; - } - - public Set getNames() { - return this.names; - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java deleted file mode 100644 index 24e181e12..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Copyright 2019-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import com.fasterxml.jackson.databind.JsonNode; -import kotlin.jvm.functions.Function0; -import kotlin.jvm.functions.Function1; -import net.jodah.typetools.TypeResolver; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.config.FunctionContextUtils; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.KotlinDetector; -import org.springframework.core.ResolvableType; -import org.springframework.messaging.Message; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; - - - -/** - * Set of utility operations to interrogate function definitions. - * - * @author Oleg Zhurakousky - * @author Andrey Shlykov - * - * @since 3.0 - */ -public final class FunctionTypeUtils { - - private static Log logger = LogFactory.getLog(FunctionTypeUtils.class); - - private FunctionTypeUtils() { - - } - - public static Type functionType(Type input, Type output) { - return ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forType(input), ResolvableType.forType(output)).getType(); - } - - public static Type consumerType(Type input) { - return ResolvableType.forClassWithGenerics(Consumer.class, - ResolvableType.forType(input)).getType(); - } - - public static Type supplierType(Type output) { - return ResolvableType.forClassWithGenerics(Supplier.class, - ResolvableType.forType(output)).getType(); - } - - /** - * Will return 'true' if the provided type is a {@link Collection} type. - * This also includes collections wrapped in {@link Message}. For example, - * If provided type is {@code Message>} this operation will return 'true'. - * - * @param type type to interrogate - * @return 'true' if this type represents a {@link Collection}. Otherwise 'false'. - */ - public static boolean isTypeCollection(Type type) { - if (Collection.class.isAssignableFrom(getRawType(type))) { - return true; - } - type = getGenericType(type); - type = type == null ? Object.class : type; - Class rawType = type instanceof ParameterizedType ? getRawType(type) : (Class) type; - return Collection.class.isAssignableFrom(rawType) || JsonNode.class.isAssignableFrom(rawType); - } - - public static boolean isTypeMap(Type type) { - if (Map.class.isAssignableFrom(getRawType(type))) { - return true; - } - type = getGenericType(type); - Class rawType = type instanceof ParameterizedType ? getRawType(type) : (Class) type; - return Map.class.isAssignableFrom(rawType); - } - - public static boolean isTypeArray(Type type) { - return getRawType(type).isArray(); - } - - public static boolean isJsonNode(Type type) { - return getRawType(type).isArray(); - } - - /** - * A convenience method identical to {@link #getImmediateGenericType(Type, int)} - * for cases when provided 'type' is {@link Publisher} or {@link Message}. - * - * @param type type to interrogate - * @return generic type if possible otherwise the same type as provided - */ - public static Type getGenericType(Type type) { - if (isPublisher(type) || isMessage(type)) { - type = getImmediateGenericType(type, 0); - } - - return TypeResolver.reify(type instanceof GenericArrayType ? type : TypeResolver.reify(type)); - } - - /** - * Effectively converts {@link Type} which could be {@link ParameterizedType} to raw Class (no generics). - * @param type actual {@link Type} instance - * @return instance of {@link Class} as raw representation of the provided {@link Type} - */ - public static Class getRawType(Type type) { - return type != null ? TypeResolver - .resolveRawClass(type instanceof GenericArrayType ? type : TypeResolver.reify(type), null) : null; - } - - /** - * Will attempt to discover functional methods on the class. It's applicable for POJOs as well as - * functional classes in `java.util.function` package. For the later the names of the methods are - * well known (`apply`, `accept` and `get`). For the former it will attempt to discover a single method - * following semantics described in (see {@link FunctionalInterface}) - * - * @param pojoFunctionClass the class to introspect - * @return functional method - */ - public static Method discoverFunctionalMethod(Class pojoFunctionClass) { - if (Supplier.class.isAssignableFrom(pojoFunctionClass)) { - return Stream.of(ReflectionUtils.getDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic() - && m.getName().equals("get")).findFirst().get(); - } - else if (Consumer.class.isAssignableFrom(pojoFunctionClass) || BiConsumer.class.isAssignableFrom(pojoFunctionClass)) { - return Stream.of(ReflectionUtils.getDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic() - && m.getName().equals("accept")).findFirst().get(); - } - else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class.isAssignableFrom(pojoFunctionClass)) { - return Stream.of(ReflectionUtils.getDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic() - && m.getName().equals("apply")).findFirst().get(); - } - - List methods = new ArrayList<>(); - ReflectionUtils.doWithMethods(pojoFunctionClass, method -> { - if (method.getDeclaringClass() == pojoFunctionClass) { - methods.add(method); - } - - }, method -> - !method.getDeclaringClass().isAssignableFrom(Object.class) - && !method.isSynthetic() && !method.isBridge() && !method.isVarArgs()); - - Assert.isTrue(methods.size() == 1, "Discovered " + methods.size() + " methods that would qualify as 'functional' - " - + methods + ".\n Class '" + pojoFunctionClass + "' is not a FunctionalInterface."); - - return methods.get(0); - } - - @SuppressWarnings("unchecked") - public static Type discoverFunctionTypeFromClass(Class functionalClass) { - if (KotlinDetector.isKotlinPresent()) { - if (Function1.class.isAssignableFrom(functionalClass)) { - return TypeResolver.reify(Function1.class, (Class>) functionalClass); - } - else if (Function0.class.isAssignableFrom(functionalClass)) { - return TypeResolver.reify(Function0.class, (Class>) functionalClass); - } - } - if (Function.class.isAssignableFrom(functionalClass)) { - for (Type superInterface : functionalClass.getGenericInterfaces()) { - if (superInterface != null && !superInterface.equals(Object.class)) { - if (superInterface.toString().contains("KStream") && ResolvableType.forType(superInterface).getGeneric(1).isArray()) { - return null; - } - } - } - return TypeResolver.reify(Function.class, (Class>) functionalClass); - } - else if (Consumer.class.isAssignableFrom(functionalClass)) { - return TypeResolver.reify(Consumer.class, (Class>) functionalClass); - } - else if (Supplier.class.isAssignableFrom(functionalClass)) { - return TypeResolver.reify(Supplier.class, (Class>) functionalClass); - } - return TypeResolver.reify(functionalClass); - } - - /** - * Discovers the function {@link Type} based on the signature of a factory method. - * For example, given the following method {@code Function, Message> uppercase()} of - * class Foo - {@code Type type = discoverFunctionTypeFromFunctionFactoryMethod(Foo.class, "uppercase");} - * - * @param clazz instance of Class containing the factory method - * @param methodName factory method name - * @return type of the function - */ - public static Type discoverFunctionTypeFromFunctionFactoryMethod(Class clazz, String methodName) { - return discoverFunctionTypeFromFunctionFactoryMethod(ReflectionUtils.findMethod(clazz, methodName)); - } - - /** - * Discovers the function {@link Type} based on the signature of a factory method. - * For example, given the following method {@code Function, Message> uppercase()} of - * class Foo - {@code Type type = discoverFunctionTypeFromFunctionFactoryMethod(Foo.class, "uppercase");} - * - * @param method factory method - * @return type of the function - */ - public static Type discoverFunctionTypeFromFunctionFactoryMethod(Method method) { - return method.getGenericReturnType(); - } - - /** - * Unlike {@link #discoverFunctionTypeFromFunctionFactoryMethod(Class, String)}, this method discovers function - * type from the well known method of Function(apply), Supplier(get) or Consumer(accept). - * @param functionMethod functional method - * @return type of the function - */ - public static Type discoverFunctionTypeFromFunctionMethod(Method functionMethod) { - Assert.isTrue( - functionMethod.getName().equals("apply") || - functionMethod.getName().equals("accept") || - functionMethod.getName().equals("get"), - "Only Supplier, Function or Consumer supported at the moment. Was " + functionMethod.getDeclaringClass()); - - if (functionMethod.getName().equals("apply")) { - return ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forMethodParameter(functionMethod, 0), - ResolvableType.forMethodReturnType(functionMethod)).getType(); - - } - else if (functionMethod.getName().equals("accept")) { - return ResolvableType.forClassWithGenerics(Consumer.class, - ResolvableType.forMethodParameter(functionMethod, 0)).getType(); - } - else { - return ResolvableType.forClassWithGenerics(Supplier.class, - ResolvableType.forMethodReturnType(functionMethod)).getType(); - } - } - - public static int getInputCount(FunctionInvocationWrapper function) { - int inputCount = function.isSupplier() ? 0 : 1; - if (inputCount > 0) { - Type inputType = function.getInputType(); - if (isMulti(inputType)) { - inputCount = ((ParameterizedType) inputType).getActualTypeArguments().length; - } - } - return inputCount; - } - - public static int getOutputCount(FunctionInvocationWrapper function) { - int outputCount = function.isConsumer() ? 0 : 1; - if (outputCount > 0) { - Type outputType = function.getOutputType(); - if (isMulti(outputType)) { - outputCount = ((ParameterizedType) outputType).getActualTypeArguments().length; - } - } - return outputCount; - } - - /** - * In the event the input type is {@link ParameterizedType} this method returns its generic type. - * @param functionType instance of function type - * @return generic type or input type - */ - public static Type getComponentTypeOfInputType(Type functionType) { - Type inputType = getInputType(functionType); - return getImmediateGenericType(inputType, 0); - } - - /** - * In the event the output type is {@link ParameterizedType} this method returns its generic type. - * @param functionType instance of function type - * @return generic type or output type - */ - public static Type getComponentTypeOfOutputType(Type functionType) { - Type inputType = getOutputType(functionType); - return getImmediateGenericType(inputType, 0); - } - - /** - * Returns input type of function type that represents Function or Consumer. - * @param functionType the Type of Function or Consumer - * @return the input type as {@link Type} - */ - @SuppressWarnings("unchecked") - public static Type getInputType(Type functionType) { - if (isSupplier(functionType)) { - logger.debug("Supplier does not have input type, returning null as input type."); - return null; - } - assertSupportedTypes(functionType); - - Type inputType; - if (functionType instanceof Class) { - functionType = Function.class.isAssignableFrom((Class) functionType) - ? TypeResolver.reify(Function.class, (Class>) functionType) - : TypeResolver.reify(Consumer.class, (Class>) functionType); - } - - inputType = functionType instanceof ParameterizedType - ? ((ParameterizedType) functionType).getActualTypeArguments()[0] - : Object.class; - - return inputType; - } - - @SuppressWarnings("rawtypes") - public static Type discoverFunctionType(Object function, String functionName, GenericApplicationContext applicationContext) { - if (function instanceof RoutingFunction) { - return FunctionContextUtils.findType(applicationContext.getBeanFactory(), functionName); - } - else if (function instanceof FunctionRegistration) { - return ((FunctionRegistration) function).getType(); - } - if (applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX)) { // for Kotlin primarily - FunctionRegistration fr = applicationContext - .getBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, FunctionRegistration.class); - return fr.getType(); - } - - boolean beanDefinitionExists = false; - String functionBeanDefinitionName = discoverDefinitionName(functionName, applicationContext); - beanDefinitionExists = applicationContext.getBeanFactory().containsBeanDefinition(functionBeanDefinitionName); - if (applicationContext.containsBean("&" + functionName)) { - Class objectType = applicationContext.getBean("&" + functionName, FactoryBean.class) - .getObjectType(); - return FunctionTypeUtils.discoverFunctionTypeFromClass(objectType); - } - - Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(function.getClass()); - if (beanDefinitionExists) { - Type t = FunctionTypeUtils.getImmediateGenericType(type, 0); - if (t == null || t == Object.class) { - type = FunctionContextUtils.findType(applicationContext.getBeanFactory(), functionBeanDefinitionName); - } - } - else if (!(type instanceof ParameterizedType)) { - String beanDefinitionName = discoverBeanDefinitionNameByQualifier(applicationContext.getBeanFactory(), functionName); - if (StringUtils.hasText(beanDefinitionName)) { - type = FunctionContextUtils.findType(applicationContext.getBeanFactory(), beanDefinitionName); - } - } - return type; - } - - public static String discoverBeanDefinitionNameByQualifier(ListableBeanFactory beanFactory, String qualifier) { - Map beanMap = BeanFactoryAnnotationUtils.qualifiedBeansOfType(beanFactory, Object.class, qualifier); - if (!CollectionUtils.isEmpty(beanMap) && beanMap.size() == 1) { - return beanMap.keySet().iterator().next(); - } - return null; - } - - @SuppressWarnings("unchecked") - public static Type getOutputType(Type functionType) { - assertSupportedTypes(functionType); - if (isConsumer(functionType)) { - logger.debug("Consumer does not have output type, returning null as output type."); - return null; - } - Type outputType; - if (functionType instanceof Class) { - functionType = Function.class.isAssignableFrom((Class) functionType) - ? TypeResolver.reify(Function.class, (Class>) functionType) - : TypeResolver.reify(Supplier.class, (Class>) functionType); - } - - outputType = functionType instanceof ParameterizedType - ? (isSupplier(functionType) ? ((ParameterizedType) functionType).getActualTypeArguments()[0] : ((ParameterizedType) functionType).getActualTypeArguments()[1]) - : Object.class; - - return outputType; - } - - public static Type getImmediateGenericType(Type type, int index) { - if (type instanceof ParameterizedType) { - return ((ParameterizedType) type).getActualTypeArguments()[index]; - } - return null; - } - - public static boolean isPublisher(Type type) { - return isFlux(type) || isMono(type); - } - - public static boolean isFlux(Type type) { - return TypeResolver.resolveRawClass(type, null) == Flux.class; - } - - public static boolean isCollectionOfMessage(Type type) { - if (isMessage(type) && isTypeCollection(type)) { - return isMessage(getImmediateGenericType(type, 0)); - } - return false; - } - - public static boolean isMessage(Type type) { - if (isPublisher(type)) { - type = getImmediateGenericType(type, 0); - } - - Class resolveRawClass = FunctionTypeUtils.getRawType(type); - if (type instanceof ParameterizedType && !Message.class.isAssignableFrom(resolveRawClass)) { - type = getImmediateGenericType(type, 0); - } - resolveRawClass = FunctionTypeUtils.getRawType(type); - if (resolveRawClass == null) { - return false; - } - return Message.class.isAssignableFrom(resolveRawClass); - } - - /** - * Determines if input argument to a Function is an array. - * @param functionType the function type - * @return true if input type is an array, otherwise false - */ - public static boolean isOutputArray(Type functionType) { - Type outputType = FunctionTypeUtils.getOutputType(functionType); - return outputType instanceof GenericArrayType || outputType instanceof Class && ((Class) outputType).isArray(); - } - - public static boolean isSupplier(Type type) { - return isOfType(type, Supplier.class); - } - - public static boolean isFunction(Type type) { - return isOfType(type, Function.class); - } - - public static boolean isConsumer(Type type) { - return isOfType(type, Consumer.class); - } - - public static boolean isMono(Type type) { - type = extractReactiveType(type); - return type == null ? false : type.getTypeName().startsWith("reactor.core.publisher.Mono"); - } - - public static boolean isMultipleArgumentType(Type type) { - if (type != null) { - if (TypeResolver.resolveRawClass(type, null).isArray()) { - return false; - } - Class clazz = TypeResolver.resolveRawClass(TypeResolver.reify(type), null); - return clazz.getName().startsWith("reactor.util.function.Tuple"); - } - return false; - } - - static Type fromFunctionMethod(Method functionalMethod) { - Type[] parameterTypes = functionalMethod.getGenericParameterTypes(); - - Type functionType = null; - switch (parameterTypes.length) { - case 0: - functionType = ResolvableType.forClassWithGenerics(Supplier.class, - ResolvableType.forMethodReturnType(functionalMethod)).getType(); - break; - case 1: - if (Void.class.isAssignableFrom(functionalMethod.getReturnType())) { - functionType = ResolvableType.forClassWithGenerics(Consumer.class, - ResolvableType.forMethodParameter(functionalMethod, 0)).getType(); - } - else { - functionType = ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forMethodParameter(functionalMethod, 0), - ResolvableType.forMethodReturnType(functionalMethod)).getType(); - } - break; - default: - throw new UnsupportedOperationException("Functional method: " + functionalMethod + " is not supported"); - } - return functionType; - } - - private static boolean isMulti(Type type) { - return type.getTypeName().startsWith("reactor.util.function.Tuple"); - } - - private static boolean isOfType(Type type, Class cls) { - if (type instanceof Class) { - return cls.isAssignableFrom((Class) type); - } - else if (type instanceof ParameterizedType) { - return isOfType(((ParameterizedType) type).getRawType(), cls); - } - return false; - } - - private static void assertSupportedTypes(Type type) { - if (type instanceof ParameterizedType) { - type = ((ParameterizedType) type).getRawType(); - Assert.isTrue(type instanceof Class, "Must be one of Supplier, Function, Consumer" - + " or FunctionRegistration. Was " + type); - } - - Class candidateType = (Class) type; - - Assert.isTrue(Supplier.class.isAssignableFrom(candidateType) - || Function.class.isAssignableFrom(candidateType) - || Consumer.class.isAssignableFrom(candidateType) - || FunctionRegistration.class.isAssignableFrom(candidateType) - || type.getTypeName().startsWith("org.springframework.context.annotation.ConfigurationClassEnhancer"), "Must be one of Supplier, Function, Consumer" - + " or FunctionRegistration. Was " + type); - } - - private static Type extractReactiveType(Type type) { - if (type instanceof ParameterizedType && FunctionRegistration.class.isAssignableFrom(((Class) ((ParameterizedType) type).getRawType()))) { - type = getImmediateGenericType(type, 0); - if (type instanceof ParameterizedType) { - type = getImmediateGenericType(type, 0); - } - } - return type; - } - - private static String discoverDefinitionName(String functionDefinition, GenericApplicationContext applicationContext) { - String[] aliases = applicationContext.getAliases(functionDefinition); - for (String alias : aliases) { - if (applicationContext.getBeanFactory().containsBeanDefinition(alias)) { - return alias; - } - } - return functionDefinition; - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionUnregistrationEvent.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionUnregistrationEvent.java deleted file mode 100644 index a8303e735..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionUnregistrationEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author Dave Syer - * - */ -@SuppressWarnings("serial") -public class FunctionUnregistrationEvent extends FunctionCatalogEvent { - - private final Class type; - - private final Set names; - - public FunctionUnregistrationEvent(Object source, Class type, Set names) { - super(source); - this.type = type; - this.names = new HashSet<>(names); - } - - public Class getType() { - return this.type; - } - - public Set getNames() { - return this.names; - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/HeaderEnricher.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/HeaderEnricher.java deleted file mode 100644 index ff8e1ebe7..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/HeaderEnricher.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import java.util.Map; -import java.util.Map.Entry; -import java.util.function.Function; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.context.expression.MapAccessor; -import org.springframework.expression.BeanResolver; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.Assert; - -/** - * Class responsible for processing `input-header-mapping-expression` - * and modifying message headers accordingly. - * - * @author Oleg Zhurakousky - * - * @since 3.1.3 - * - */ -class HeaderEnricher implements Function { - - protected Log logger = LogFactory.getLog(HeaderEnricher.class); - - private final Map> headerExpressions; - - private final SpelExpressionParser spelParser = new SpelExpressionParser(); - - private final StandardEvaluationContext evalContext = new StandardEvaluationContext(); - - @SuppressWarnings({ "rawtypes", "unchecked" }) - HeaderEnricher(Map headerExpressions, @Nullable BeanResolver beanResolver) { - Assert.notEmpty(headerExpressions, "'headerExpressions' must not be null or empty"); - this.headerExpressions = headerExpressions; - this.evalContext.addPropertyAccessor(new MapAccessor()); - if (beanResolver != null) { - this.evalContext.setBeanResolver(beanResolver); - } - } - - @Override - public Object apply(Object input) { - if (input instanceof Message) { - MessageBuilder messageBuilder = MessageBuilder.fromMessage((Message) input); - Map mappings = this.headerExpressions.get("0"); - for (Entry keyValueExpressionEntry : mappings.entrySet()) { - Expression expression = this.spelParser.parseExpression(keyValueExpressionEntry.getValue()); - try { - Object value = expression.getValue(this.evalContext, input, Object.class); - messageBuilder.setHeader(keyValueExpressionEntry.getKey(), value); - } - catch (Exception e) { - String message = "Failed while evaluating expression \"" + keyValueExpressionEntry.getValue() + "\" on incoming message"; - if (logger.isDebugEnabled()) { - logger.warn(message + ": " + input, e); - } - else { - logger.warn(message); - } - } - } - input = messageBuilder.build(); - } - - return input; - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java deleted file mode 100644 index 08a91bce8..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java +++ /dev/null @@ -1,1537 +0,0 @@ -/* - * Copyright 2019-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.util.function.Tuples; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.FunctionProperties.FunctionConfigurationProperties; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.PostProcessingFunction; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.cloud.function.core.FunctionInvocationHelper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.core.ResolvableType; -import org.springframework.core.convert.ConversionService; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.http.HttpHeaders; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.converter.CompositeMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.MimeTypeUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - - -/** - * Implementation of {@link FunctionCatalog} and {@link FunctionRegistry} which - * does not depend on Spring's {@link BeanFactory}. - * Each function must be registered with it explicitly to benefit from features - * such as type conversion, composition, POJO etc. - * - * @author Oleg Zhurakousky - * @author Roman Samarev - * @author Soby Chacko - * @author Chris Bono - */ -public class SimpleFunctionRegistry implements FunctionRegistry { - protected Log logger = LogFactory.getLog(this.getClass()); - /* - * - do we care about FunctionRegistration after it's been registered? What additional value does it bring? - * - */ - - private final Set> functionRegistrations = new CopyOnWriteArraySet<>(); - - private final Map wrappedFunctionDefinitions = new HashMap<>(); - - private final ConversionService conversionService; - - private final CompositeMessageConverter messageConverter; - - private final JsonMapper jsonMapper; - - private final FunctionInvocationHelper> functionInvocationHelper; - - private final FunctionProperties functionProperties; - - @Autowired(required = false) - private FunctionAroundWrapper functionAroundWrapper; - - public SimpleFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, JsonMapper jsonMapper, - @Nullable FunctionProperties functionProperties, - @Nullable FunctionInvocationHelper> functionInvocationHelper) { - Assert.notNull(messageConverter, "'messageConverter' must not be null"); - Assert.notNull(jsonMapper, "'jsonMapper' must not be null"); - this.conversionService = conversionService; - this.jsonMapper = jsonMapper; - this.messageConverter = messageConverter; - this.functionInvocationHelper = functionInvocationHelper; - this.functionProperties = functionProperties; - } - - /** - * Will add provided {@link MessageConverter}s to the head of the stack of the existing MessageConverters. - * - * @param messageConverters list of {@link MessageConverter}s. - */ - public void addMessageConverters(Collection messageConverters) { - if (!CollectionUtils.isEmpty(messageConverters)) { - this.messageConverter.getConverters().addAll(0, messageConverters); - } - } - - public SimpleFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, JsonMapper jsonMapper) { - this(conversionService, messageConverter, jsonMapper, null, null); - } - - @SuppressWarnings("unchecked") - @Override - public T lookup(Class type, String functionDefinition, String... expectedOutputMimeTypes) { - functionDefinition = this.normalizeFunctionDefinition(functionDefinition); - FunctionInvocationWrapper function = this.doLookup(type, functionDefinition, expectedOutputMimeTypes); - if (logger.isDebugEnabled()) { - if (function != null) { - logger.debug("Located function: " + function); - } - else { - logger.debug("Failed to locate function: " + functionDefinition); - } - } - return (T) function; - } - - @Override - public void register(FunctionRegistration registration) { - if (!isRegistrationEligible(registration)) { - return; - } - Assert.notNull(registration, "'registration' must not be null"); - if (logger.isDebugEnabled()) { - logger.debug("Registering function " + registration.getNames()); - } - this.functionRegistrations.add(registration); - } - - @SuppressWarnings("rawtypes") - private boolean isRegistrationEligible(FunctionRegistration registration) { - if (this.functionProperties != null) { - for (String definition : this.functionProperties.getIneligibleDefinitions()) { - if (registration.getTarget().getClass().getName().equals(definition)) { - return false; - } - else if (registration.getNames().contains(definition) || registration.getTarget().getClass().getName().contains(definition)) { - return false; - } - } - } - return true; - } - - boolean isFunctionDefinitionEligible(String functionDefinition) { - if (this.functionProperties != null) { - for (String definition : this.functionProperties.getIneligibleDefinitions()) { - if (functionDefinition.contains(definition)) { - return false; - } - } - } - return true; - } - - //----- - - @Override - public Set getNames(Class type) { - return this.functionRegistrations.stream().flatMap(fr -> fr.getNames().stream()).collect(Collectors.toSet()); - } - - @Override - public int size() { - return this.functionRegistrations.size(); - } - - /* - * - */ - protected boolean containsFunction(String functionName) { - return this.functionRegistrations.stream().anyMatch(reg -> reg.getNames().contains(functionName)); - } - - /* - * - */ - @SuppressWarnings("unchecked") - T doLookup(Class type, String functionDefinition, String[] expectedOutputMimeTypes) { - FunctionInvocationWrapper function = this.wrappedFunctionDefinitions.get(functionDefinition); - if (function == null) { - function = this.compose(type, functionDefinition); - } - - if (function != null) { - if (!ObjectUtils.isEmpty(expectedOutputMimeTypes)) { - function.expectedOutputContentType = expectedOutputMimeTypes; - } - } - else if (logger.isDebugEnabled()) { - logger.debug("Function '" + functionDefinition + "' is not found in cache"); - } - - return (T) function; - } - - /** - * This method will make sure that if there is only one function in catalog - * it can be looked up by any name or no name. - * It does so by attempting to determine the default function name - * (the only function in catalog) and checking if it matches the provided name - * replacing it if it does not. - */ - String normalizeFunctionDefinition(String functionDefinition) { - functionDefinition = StringUtils.hasText(functionDefinition) - ? functionDefinition.replaceAll(",", "|") - : System.getProperty(FunctionProperties.FUNCTION_DEFINITION, ""); - - Set names = this.getNames(null); - if (!names.contains(functionDefinition)) { - List eligibleFunction = names.stream() - .filter(name -> !RoutingFunction.FUNCTION_NAME.equals(name)) - .filter(name -> !RoutingFunction.DEFAULT_ROUTE_HANDLER.equals(name)) - .collect(Collectors.toList()); - if (eligibleFunction.size() == 1 - && !eligibleFunction.get(0).equals(functionDefinition) - && !functionDefinition.contains("|") - && !eligibleFunction.get(0).startsWith("&")) { - functionDefinition = eligibleFunction.get(0); - } - } - return functionDefinition; - } - - /* - * - */ - private FunctionInvocationWrapper findFunctionInFunctionRegistrations(String functionName) { - FunctionRegistration functionRegistration = this.functionRegistrations.stream() - .filter(fr -> fr.getNames().contains(functionName)) - .findFirst() - .orElseGet(() -> null); - FunctionInvocationWrapper function = functionRegistration != null - ? this.invocationWrapperInstance(functionName, functionRegistration.getTarget(), functionRegistration.getType()) - : null; - if (functionRegistration != null) { - Object userFunction = functionRegistration.getUserFunction(); - if (userFunction instanceof BiConsumer && function != null) { - function.setWrappedBiConsumer(true); - } - } - if (functionRegistration != null && functionRegistration.getProperties().containsKey("singleton")) { - try { - function.isSingleton = Boolean.parseBoolean(functionRegistration.getProperties().get("singleton")); - } - catch (Exception e) { - // ignore - } - } - return function; - } - - /* - * - */ - private FunctionInvocationWrapper compose(Class type, String functionDefinition) { - String[] functionNames = StringUtils.delimitedListToStringArray(functionDefinition.replaceAll(",", "|").trim(), "|"); - FunctionInvocationWrapper composedFunction = null; - - for (String functionName : functionNames) { - FunctionInvocationWrapper function = this.findFunctionInFunctionRegistrations(functionName); - if (function == null) { - return null; - } - else { - if (composedFunction == null) { - composedFunction = function; - } - else { - FunctionInvocationWrapper andThenFunction = - invocationWrapperInstance(functionName, function.getTarget(), function.inputType, function.outputType); - composedFunction = (FunctionInvocationWrapper) composedFunction.andThen((Function) andThenFunction); - } - composedFunction = this.enrichInputIfNecessary(composedFunction); - composedFunction = this.enrichOutputIfNecessary(composedFunction); - if (composedFunction.isSingleton) { - this.wrappedFunctionDefinitions.put(composedFunction.functionDefinition, composedFunction); - } - } - } - if (logger.isDebugEnabled()) { - logger.debug("Composed function " + composedFunction); - } - return composedFunction; - } - - private FunctionInvocationWrapper enrichInputIfNecessary(FunctionInvocationWrapper composedFunction) { - if (this.functionProperties == null) { - return composedFunction; - } - String functionDefinition = composedFunction.getFunctionDefinition(); - Map configurationProperties = this.functionProperties.getConfiguration(); - if (!CollectionUtils.isEmpty(configurationProperties)) { - FunctionConfigurationProperties configuration = configurationProperties - .get(functionDefinition.replace("|", "").replace(",", "")); - if (configuration != null) { - if (!CollectionUtils.isEmpty(configuration.getInputHeaderMappingExpression())) { - BeanFactoryResolver beanResolver = this.functionProperties.getApplicationContext() != null - ? new BeanFactoryResolver(this.functionProperties.getApplicationContext()) - : null; - HeaderEnricher enricher = new HeaderEnricher(configuration.getInputHeaderMappingExpression(), beanResolver); - FunctionInvocationWrapper w = new FunctionInvocationWrapper("inputHeaderEnricher", enricher, Message.class, Message.class); - composedFunction = (FunctionInvocationWrapper) w.andThen((Function) composedFunction); - composedFunction.functionDefinition = functionDefinition; - } - } - } - return composedFunction; - } - - private FunctionInvocationWrapper enrichOutputIfNecessary(FunctionInvocationWrapper composedFunction) { - if (this.functionProperties == null) { - return composedFunction; - } - String functionDefinition = composedFunction.getFunctionDefinition(); - Map configurationProperties = this.functionProperties.getConfiguration(); - if (!CollectionUtils.isEmpty(configurationProperties)) { - FunctionConfigurationProperties configuration = configurationProperties - .get(functionDefinition.replace("|", "").replace(",", "")); - if (configuration != null) { - if (!CollectionUtils.isEmpty(configuration.getOutputHeaderMappingExpression())) { - BeanFactoryResolver beanResolver = this.functionProperties.getApplicationContext() != null - ? new BeanFactoryResolver(this.functionProperties.getApplicationContext()) - : null; - HeaderEnricher enricher = new HeaderEnricher(configuration.getOutputHeaderMappingExpression(), beanResolver); - Type mesageType = ResolvableType.forClassWithGenerics(Message.class, Object.class).getType(); - FunctionInvocationWrapper enricherWrapper = new FunctionInvocationWrapper("outputHeaderEnricher", enricher, mesageType, mesageType); - composedFunction = (FunctionInvocationWrapper) composedFunction.andThen((Function) enricherWrapper); - composedFunction.functionDefinition = functionDefinition; - } - } - } - return composedFunction; - } - - /* - * - */ - private FunctionInvocationWrapper invocationWrapperInstance(String functionDefinition, Object target, Type inputType, Type outputType) { - return new FunctionInvocationWrapper(functionDefinition, target, inputType, outputType); - } - - /* - * - */ - private FunctionInvocationWrapper invocationWrapperInstance(String functionDefinition, Object target, Type functionType) { - return invocationWrapperInstance(functionDefinition, target, - FunctionTypeUtils.isSupplier(functionType) ? null : FunctionTypeUtils.getInputType(functionType), - FunctionTypeUtils.getOutputType(functionType)); - } - - /** - * - */ - @SuppressWarnings("rawtypes") - public class FunctionInvocationWrapper implements Function, Consumer, Supplier, Runnable { - - private final Object target; - - private Type inputType; - - private final Type outputType; - - private String functionDefinition; - - private boolean composed; - - private boolean message; - - private String[] expectedOutputContentType; - - private boolean skipInputConversion; - - private boolean skipOutputConversion; - - private boolean isSingleton = true; - - private boolean propagateInputHeaders; - - private boolean wrapped; - - private final ThreadLocal> unconvertedResult = new ThreadLocal<>(); - - private PostProcessingFunction postProcessor; - - /* - * This is primarily to support Stream's ability to access - * un-converted payload (e.g., to evaluate expression on some attribute of a payload) - * It is not intended to remain here and will be removed as soon as particular elements - * of stream will be refactored to address this. - */ - private Function enhancer; - - private boolean wrappedBiConsumer; - - FunctionInvocationWrapper(String functionDefinition, Object target, Type inputType, Type outputType) { - if (target instanceof PostProcessingFunction) { - this.postProcessor = (PostProcessingFunction) target; - } - this.target = target; - this.inputType = this.normalizeType(inputType); - this.outputType = this.normalizeType(outputType); - this.functionDefinition = functionDefinition; - this.message = this.inputType != null && FunctionTypeUtils.isMessage(this.inputType); - if (functionProperties != null) { - Map funcConfiguration = functionProperties.getConfiguration(); - if (!CollectionUtils.isEmpty(funcConfiguration)) { - FunctionConfigurationProperties configuration = funcConfiguration.get(functionDefinition); - if (configuration != null) { - propagateInputHeaders = configuration.isCopyInputHeaders(); - } - } - } - } - - @SuppressWarnings("unchecked") - public void postProcess() { - if (this.postProcessor != null) { - Message result = this.unconvertedResult.get(); - if (result != null) { - try { - this.postProcessor.postProcess(result); - } - catch (Exception ex) { - logger.warn("Failed to post process function " - + this.functionDefinition + "; Result of the invocation before post processing is " + result, ex); - } - finally { - this.unconvertedResult.remove(); - } - } - } - } - - public boolean isWrappedBiConsumer() { - return wrappedBiConsumer; - } - - public void setWrappedBiConsumer(boolean wrappedBiConsumer) { - this.wrappedBiConsumer = wrappedBiConsumer; - } - - public boolean isSkipOutputConversion() { - return skipOutputConversion; - } - - - public boolean isPrototype() { - return !this.isSingleton; - } - - public void setSkipInputConversion(boolean skipInputConversion) { - if (logger.isDebugEnabled() && skipInputConversion) { - logger.debug("'skipInputConversion' was explicitely set to true. No input conversion will be attempted"); - } - this.skipInputConversion = skipInputConversion; - } - - public void setSkipOutputConversion(boolean skipOutputConversion) { - if (logger.isDebugEnabled() && skipOutputConversion) { - logger.debug("'skipOutputConversion' was explicitely set to true. No output conversion will be attempted"); - } - this.skipOutputConversion = skipOutputConversion; - } - - /** - * !!! INTERNAL USE ONLY !!! - * This is primarily to support s-c-Stream's ability to access - * un-converted payload (e.g., to evaluate expression on some attribute of a payload) - * It is not intended to remain here and will be removed as soon as particular elements - * of stream will be refactored to address this. - */ - public Function getEnhancer() { - return this.enhancer; - } - - public Type getOutputType() { - return this.outputType; - } - - /** - * !!! INTERNAL USE ONLY !!! - * This is primarily to support s-c-Stream's ability to access - * un-converted payload (e.g., to evaluate expression on some attribute of a payload) - * It is not intended to remain here and will be removed as soon as particular elements - * of stream will be refactored to address this. - */ - public void setEnhancer(Function enhancer) { - this.enhancer = enhancer; - } - - public Object getTarget() { - return target; - } - - public Type getInputType() { - return this.inputType; - } - - /** - * Return the actual {@link Type} of the item of the provided type. - * This method is context specific and is not a general purpose utility method. The context is that the provided - * {@link Type} may represent the input/output of a function where such type could be wrapped in - * {@link Message}, {@link Flux} or {@link Mono}, so this method returns generic value of such type or itself if not wrapped. - * @param type typically input or output Type of the function (see {@link #getInputType()} or {@link #getOutputType()}. - * @return the type of the item if wrapped otherwise the provided type. - */ - public Type getItemType(Type type) { - if (FunctionTypeUtils.isPublisher(type) || FunctionTypeUtils.isMessage(type) || FunctionTypeUtils.isTypeCollection(type)) { - type = FunctionTypeUtils.getGenericType(type); - } - if (FunctionTypeUtils.isMessage(type)) { - type = FunctionTypeUtils.getGenericType(type); - } - return type; - } - - public Class getRawOutputType() { - return this.outputType == null ? null : FunctionTypeUtils.getRawType(this.outputType); - } - - public Class getRawInputType() { - return this.inputType == null ? null : FunctionTypeUtils.getRawType(this.inputType); - } - - /** - * - */ - @Override - public Object apply(Object input) { - if (logger.isDebugEnabled() && !(input instanceof Publisher)) { - logger.debug("Invoking function " + this); - } - - Object result; - if (functionAroundWrapper != null && !this.wrapped) { - this.wrapped = true; - result = functionAroundWrapper.apply(input, this); - } - else { - result = this.doApply(input); - } - - return result; - } - - @Override - public Object get() { - return this.apply(null); - } - - @Override - public void accept(Object input) { - this.apply(input); - } - - @Override - public void run() { - this.apply(null); - } - - public boolean isConsumer() { - return this.outputType == null; - } - - public boolean isSupplier() { - return this.inputType == null; - } - - public boolean isFunction() { - return this.inputType != null && this.outputType != null; - } - - public boolean isInputTypePublisher() { - return this.isTypePublisher(this.inputType); - } - - public boolean isOutputTypePublisher() { - return this.isTypePublisher(this.outputType); - } - - public boolean isInputTypeMessage() { - boolean b = this.message || this.isRoutingFunction(); - return b; - } - - public boolean isOutputTypeMessage() { - return FunctionTypeUtils.isMessage(this.outputType); - } - - - public boolean isRoutingFunction() { - return this.target instanceof RoutingFunction; - } - - /* - * - */ - @SuppressWarnings("unchecked") - @Override - public Function andThen(Function after) { - Assert.isTrue(after instanceof FunctionInvocationWrapper, "Composed function must be an instanceof FunctionInvocationWrapper."); - if (FunctionTypeUtils.isMultipleArgumentType(this.inputType) - || FunctionTypeUtils.isMultipleArgumentType(this.outputType) - || FunctionTypeUtils.isMultipleArgumentType(((FunctionInvocationWrapper) after).inputType) - || FunctionTypeUtils.isMultipleArgumentType(((FunctionInvocationWrapper) after).outputType)) { - throw new UnsupportedOperationException("Composition of functions with multiple arguments is not supported at the moment"); - } - - Function rawComposedFunction = v -> ((FunctionInvocationWrapper) after).doApply(doApply(v)); - - FunctionInvocationWrapper afterWrapper = (FunctionInvocationWrapper) after; - - Type composedFunctionType; - if (afterWrapper.outputType == null) { - composedFunctionType = (this.inputType == null) ? - ResolvableType.forClassWithGenerics(Supplier.class, ResolvableType.forType(Object.class)).getType() : - ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forType(this.inputType)).getType(); - } - else if (this.inputType == null && afterWrapper.outputType != null) { - ResolvableType composedOutputType; - if (FunctionTypeUtils.isFlux(this.outputType)) { - composedOutputType = ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(afterWrapper.outputType)); - } - else if (FunctionTypeUtils.isMono(this.outputType)) { - composedOutputType = ResolvableType.forClassWithGenerics(Mono.class, ResolvableType.forType(afterWrapper.outputType)); - } - else { - composedOutputType = ResolvableType.forType(afterWrapper.outputType); - } - - composedFunctionType = ResolvableType.forClassWithGenerics(Supplier.class, composedOutputType).getType(); - } - else if (this.outputType == null) { - throw new IllegalArgumentException("Can NOT compose anything with Consumer"); - } - else { - composedFunctionType = ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forType(this.inputType), - ResolvableType.forType(((FunctionInvocationWrapper) after).outputType)).getType(); - } - - String composedName = this.functionDefinition + "|" + afterWrapper.functionDefinition; - FunctionInvocationWrapper composedFunction = invocationWrapperInstance(composedName, rawComposedFunction, composedFunctionType); - composedFunction.composed = true; - if (((FunctionInvocationWrapper) after).target instanceof PostProcessingFunction) { - composedFunction.postProcessor = (PostProcessingFunction) ((FunctionInvocationWrapper) after).target; - } - - return (Function) composedFunction; - } - - /** - * Returns the definition of this function. - * @return function definition - */ - public String getFunctionDefinition() { - return this.functionDefinition; - } - - /* - * - */ - @Override - public String toString() { - return this.functionDefinition + (this.isComposed() ? "" : "<" + this.inputType + ", " + this.outputType + ">"); - } - - /** - * Returns true if this function wrapper represents a composed function. - * @return true if this function wrapper represents a composed function otherwise false - */ - boolean isComposed() { - return this.composed; - } - - /* - * - */ - @SuppressWarnings("unchecked") - Object doApply(Object input) { - Object result; - - input = this.fluxifyInputIfNecessary(input); - - Object convertedInput = this.convertInputIfNecessary(input, this.inputType); - - if (this.isRoutingFunction() || this.isComposed()) { - result = ((Function) this.target).apply(convertedInput); - } - else if (this.isSupplier()) { - result = ((Supplier) this.target).get(); - } - else if (this.isConsumer()) { - result = this.invokeConsumer(convertedInput); - } - else { // Function - result = this.invokeFunction(convertedInput); - } - - if (this.postProcessor != null) { - this.unconvertedResult.set((Message) result); - } - - if (result != null && this.outputType != null) { - result = this.convertOutputIfNecessary(result, this.outputType, this.expectedOutputContentType); - } - return result; - } - - /* - * - */ - private boolean isTypePublisher(Type type) { - return type != null && FunctionTypeUtils.isPublisher(type); - } - - /** - * Will return Object.class if type is represented as TypeVariable(T) or WildcardType(?). - */ - private Type normalizeType(Type type) { - if (type != null) { - return !(type instanceof TypeVariable) && !(type instanceof WildcardType) ? type : Object.class; - } - return type; - } - - /* - * - */ - private Class getRawClassFor(@Nullable Type type) { - return type instanceof TypeVariable || type instanceof WildcardType - ? Object.class - : FunctionTypeUtils.getRawType(type); - } - - /** - * Will wrap the result in a Message if necessary and will copy input headers to the output message. - */ - private Object enrichInvocationResultIfNecessary(Object input, Object result) { - if (result != null && !(result instanceof Publisher) && input instanceof Message) { - if (result instanceof Message) { - if (functionInvocationHelper != null && CloudEventMessageUtils.isCloudEvent(((Message) input))) { - result = functionInvocationHelper.postProcessResult(result, (Message) input); - } - } - else { - if (functionInvocationHelper != null && CloudEventMessageUtils.isCloudEvent(((Message) input))) { - result = functionInvocationHelper.postProcessResult(result, (Message) input); - } - else if (!FunctionTypeUtils.isCollectionOfMessage(this.outputType)) { - result = MessageBuilder.withPayload(result).copyHeaders(this.sanitizeHeaders(((Message) input).getHeaders())).build(); - } - } - } - return result; - } - - /* - * Will ensure no headers with null values are copied. - */ - private Map sanitizeHeaders(MessageHeaders headers) { - Map sanitizedHeaders = new HashMap<>(); - headers.forEach((k, v) -> { - if (v != null) { - sanitizedHeaders.put(k, v); - } - }); - return sanitizedHeaders; - } - - @SuppressWarnings("unchecked") - private Object fluxifyInputIfNecessary(Object input) { - if (input instanceof Message && !((Message) input).getHeaders().containsKey("user-agent") && this.isConsumer() && !this.isInputTypePublisher()) { - return input; - } - if (FunctionTypeUtils.isMultipleArgumentType(this.inputType)) { - return input; - } - - if (!this.isRoutingFunction() && !(input instanceof Publisher)) { - Object payload = input; - var treatPayloadAsPlainText = false; - if (input instanceof Message msg) { - if (msg.getHeaders().containsKey("payload")) { - payload = msg.getHeaders().get("payload"); - } - else { - payload = msg.getPayload(); - } - treatPayloadAsPlainText = contentTypeHeaderValue(msg).equals(MimeTypeUtils.TEXT_PLAIN_VALUE); - } - - if ((!treatPayloadAsPlainText && JsonMapper.isJsonStringRepresentsCollection(payload)) - && !FunctionTypeUtils.isTypeCollection(this.inputType) - && !FunctionTypeUtils.isTypeArray(this.inputType)) { - MessageHeaders headers = ((Message) input).getHeaders(); - Collection collectionPayload = jsonMapper.fromJson(payload, Collection.class); - Class inputClass = FunctionTypeUtils.getRawType(this.inputType); - if (this.isInputTypeMessage()) { - inputClass = FunctionTypeUtils.getRawType(FunctionTypeUtils.getImmediateGenericType(this.inputType, 0)); - } - - if (!inputClass.isAssignableFrom(Object.class) && !inputClass.isAssignableFrom(byte[].class)) { - logger.debug("Converting JSON string representing collection to a list of Messages. Function '" - + this + "' will be invoked iteratively"); - input = collectionPayload.stream() - .map(p -> MessageBuilder.withPayload(p).copyHeaders(headers).build()) - .collect(Collectors.toList()); - } - } - } - - if (this.isTypePublisher(this.inputType) && !(input instanceof Publisher)) { - if (input == null) { - input = FunctionTypeUtils.isMono(this.inputType) ? Mono.empty() : Flux.empty(); - } - else if (input instanceof Message && ((Message) input).getPayload() instanceof Iterable) { - input = FunctionTypeUtils.isMono(this.inputType) ? Mono.just(input) : Flux.just(input).flatMap(v -> { - if (logger.isDebugEnabled()) { - logger.debug("Creating Flux from Iterable: " + ((Message) v).getPayload()); - } - return Flux.fromIterable((Iterable) ((Message) v).getPayload()); - }); - } - else if (input instanceof Iterable) { - input = FunctionTypeUtils.isMono(this.inputType) ? Mono.just(input) : Flux.fromIterable((Iterable) input); - - } - else { - input = FunctionTypeUtils.isMono(this.inputType) ? Mono.just(input) : Flux.just(input); - } - } - else if (!(input instanceof Publisher) && input instanceof Iterable && !FunctionTypeUtils.isTypeCollection(this.inputType)) { - input = Flux.fromIterable((Iterable) input); - } - return input; - } - - private String contentTypeHeaderValue(Message msg) { - var contentType = msg.getHeaders().get(MessageHeaders.CONTENT_TYPE); - if (contentType == null) { - contentType = msg.getHeaders().get(HttpHeaders.CONTENT_TYPE); - if (contentType == null) { - contentType = msg.getHeaders().get(HttpHeaders.CONTENT_TYPE.toLowerCase()); - } - } - return Objects.toString(contentType); - } - - @SuppressWarnings("unchecked") - private Object invokeFunction(Object convertedInput) { - Object result; - if (!this.isTypePublisher(this.inputType) && convertedInput instanceof Publisher) { - result = convertedInput instanceof Mono - ? Mono.from((Publisher) convertedInput).map(value -> this.invokeFunctionAndEnrichResultIfNecessary(value)) - .doOnError(ex -> logger.error("Failed to invoke function '" + this.functionDefinition + "'", (Throwable) ex)) - : Flux.from((Publisher) convertedInput).map(value -> this.invokeFunctionAndEnrichResultIfNecessary(value)) - .doOnError(ex -> logger.error("Failed to invoke function '" + this.functionDefinition + "'", (Throwable) ex)); - } - else { - result = this.invokeFunctionAndEnrichResultIfNecessary(convertedInput); - if (result instanceof Flux) { - result = ((Flux) result).doOnError(ex -> logger.error("Failed to invoke function '" - + this.functionDefinition + "'", (Throwable) ex)); - } - else if (result instanceof Mono) { - result = ((Mono) result).doOnError(ex -> logger.error("Failed to invoke function '" - + this.functionDefinition + "'", (Throwable) ex)); - } - } - return result; - } - - /* - * - */ - @SuppressWarnings("unchecked") - private Object invokeFunctionAndEnrichResultIfNecessary(Object value) { - AtomicReference> firstInputMessage = new AtomicReference<>(); - - Object inputValue; - if (value instanceof Flux) { - inputValue = ((Flux) value).map(v -> { - if (v instanceof OriginalMessageHolder && firstInputMessage.get() == null) { - firstInputMessage.set(((OriginalMessageHolder) v).getOriginalMessage()); - } - return this.extractValueFromOriginalValueHolderIfNecessary(v); - }); - } - else if (value instanceof Mono) { - inputValue = ((Mono) value).map(v -> { - if (v instanceof OriginalMessageHolder) { - firstInputMessage.set(((OriginalMessageHolder) v).getOriginalMessage()); - } - return this.extractValueFromOriginalValueHolderIfNecessary(v); - }); - } - else { - inputValue = this.extractValueFromOriginalValueHolderIfNecessary(value); - } - - if (!(this.target instanceof PassThruFunction) && inputValue instanceof Message && !this.isInputTypeMessage()) { - inputValue = ((Message) inputValue).getPayload(); - } - - if (logger.isDebugEnabled()) { - logger.debug("Invoking function: " + this + "with input type: " + this.getInputType()); - } - - Object result; - if (inputValue != null && inputValue.getClass().getName().equals("org.springframework.kafka.support.KafkaNull")) { - result = ((Function) this.target).apply(null); - } - else { - result = ((Function) this.target).apply(inputValue); - } - - if (result instanceof Publisher && functionInvocationHelper != null) { - result = this.postProcessFunction((Publisher) result, firstInputMessage); - } - - return value instanceof OriginalMessageHolder - ? this.enrichInvocationResultIfNecessary(((OriginalMessageHolder) value).getOriginalMessage(), result) - : result; - } - - @SuppressWarnings("unchecked") - private Publisher postProcessFunction(Publisher result, AtomicReference> firstInputMessage) { - if (FunctionTypeUtils.isPublisher(this.inputType) && FunctionTypeUtils.isPublisher(this.outputType)) { - if (!FunctionTypeUtils.getRawType(FunctionTypeUtils.getImmediateGenericType(this.inputType, 0)) - .isAssignableFrom(Void.class) - && !FunctionTypeUtils.getRawType(FunctionTypeUtils.getImmediateGenericType(this.outputType, 0)) - .isAssignableFrom(Void.class)) { - - if (result instanceof Mono) { - return Mono.from((result)).map(v -> { - if (firstInputMessage.get() != null && CloudEventMessageUtils - .isCloudEvent(firstInputMessage.get())) { - return functionInvocationHelper.postProcessResult(v, - firstInputMessage.get()); - } - return v; - }); - } - else { - return Flux.from((result)).map(v -> { - if (firstInputMessage.get() != null && CloudEventMessageUtils - .isCloudEvent(firstInputMessage.get())) { - return functionInvocationHelper.postProcessResult(v, - firstInputMessage.get()); - } - return v; - }); - } - } - } - - return result; - } - - /* - * - */ - @SuppressWarnings("unchecked") - private Object invokeConsumer(Object convertedInput) { - Object result = null; - if (this.isTypePublisher(this.inputType)) { - if (convertedInput instanceof Flux) { - result = ((Flux) convertedInput) - .transform(flux -> { - flux = Flux.from((Publisher) flux).map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)); - ((Consumer) this.target).accept(flux); - return Mono.ignoreElements((Flux) flux); - }).then(); - } - else { - result = ((Mono) convertedInput) - .transform(mono -> { - mono = Mono.from((Publisher) mono).map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)); - ((Consumer) this.target).accept(mono); - return Mono.ignoreElements((Mono) mono); - }).then(); - } - } - else if (convertedInput instanceof Publisher) { - result = convertedInput instanceof Mono - ? Mono.from((Publisher) convertedInput) - .map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)) - .doOnNext((Consumer) this.target).then() - : Flux.from((Publisher) convertedInput) - .map(v -> this.extractValueFromOriginalValueHolderIfNecessary(v)) - .doOnNext((Consumer) this.target).then(); - } - else { - Object extractedValue = this.extractValueFromOriginalValueHolderIfNecessary(convertedInput); - ((Consumer) this.target).accept(extractedValue); - } - return result; - } - - private Object extractValueFromOriginalValueHolderIfNecessary(Object input) { - if (input instanceof OriginalMessageHolder) { - input = ((OriginalMessageHolder) input).getValue(); - } - return input; - } - - /** - * This operation will parse value coming in as Tuples to Object[]. - */ - private Object[] parseMultipleValueArguments(Object multipleValueArgument, int argumentCount) { - Object[] parsedArgumentValues = new Object[argumentCount]; - if (multipleValueArgument.getClass().getName().startsWith("reactor.util.function.Tuple")) { - for (int i = 0; i < argumentCount; i++) { - Expression parsed = new SpelExpressionParser().parseExpression("getT" + (i + 1) + "()"); - Object outputArgument = parsed.getValue(multipleValueArgument); - parsedArgumentValues[i] = outputArgument; - } - return parsedArgumentValues; - } - throw new UnsupportedOperationException("At the moment only Tuple-based function are supporting multiple arguments"); - } - - @SuppressWarnings("unchecked") - private boolean isInputConversionNecessary(Object input, Type type) { - if (type == null || this.getRawClassFor(type) == Void.class || this.target instanceof RoutingFunction || this.isComposed() || this.target instanceof PassThruFunction) { - if (this.getRawClassFor(type) == Void.class) { - if (input instanceof Message) { - input = ((Message) input).getPayload(); - if (input instanceof Optional) { - input = ((Optional) input).orElseGet(() -> null); - } - } - Assert.isNull(input, "Can't have non-null input with Void input type."); - } - return false; - } - return true; - } - /* - * - */ - private Object convertInputIfNecessary(Object input, Type type) { - if (!this.isInputConversionNecessary(input, type)) { - return input; - } - - Object convertedInput = null; - if (input instanceof Publisher) { - convertedInput = this.convertInputPublisherIfNecessary((Publisher) input, type); - } - else if (FunctionTypeUtils.isMultipleArgumentType(type)) { - Type[] inputTypes = ((ParameterizedType) type).getActualTypeArguments(); - Object[] multipleValueArguments = this.parseMultipleValueArguments(input, inputTypes.length); - Object[] convertedInputs = new Object[inputTypes.length]; - for (int i = 0; i < multipleValueArguments.length; i++) { - Object cInput = this.convertInputIfNecessary(multipleValueArguments[i], inputTypes[i]); - convertedInputs[i] = cInput; - } - convertedInput = Tuples.fromArray(convertedInputs); - } - else if (this.skipInputConversion) { - convertedInput = this.isInputTypeMessage() - ? input - : new OriginalMessageHolder(((Message) input).getPayload(), (Message) input); - } - else if (input instanceof Message) { - input = this.filterOutHeaders((Message) input); - if (((Message) input).getPayload().getClass().getName().equals("org.springframework.kafka.support.KafkaNull")) { - return input; - } - - if (functionInvocationHelper != null) { - input = functionInvocationHelper.preProcessInput((Message) input, messageConverter); - } - - convertedInput = this.convertInputMessageIfNecessary((Message) input, type); - if (convertedInput == null) { // give ConversionService a chance - convertedInput = this.convertNonMessageInputIfNecessary(type, ((Message) input).getPayload(), false); - } - if (convertedInput != null && !FunctionTypeUtils.isMultipleArgumentType(this.inputType)) { - convertedInput = !convertedInput.equals(input) - ? new OriginalMessageHolder(convertedInput, (Message) input) - : convertedInput; - } - if (convertedInput != null && logger.isDebugEnabled()) { - logger.debug("Converted Message: " + input + " to: " + - (convertedInput instanceof OriginalMessageHolder ? ((OriginalMessageHolder) convertedInput).value.getClass() : convertedInput)); - } - } - else { - convertedInput = this.convertNonMessageInputIfNecessary(type, input, JsonMapper.isJsonString(input)); - if (convertedInput != null && logger.isDebugEnabled()) { - logger.debug("Converted input: " + input + " to: " + convertedInput); - } - } - // wrap in Message if necessary - if (this.isWrapConvertedInputInMessage(convertedInput)) { - convertedInput = MessageBuilder.withPayload(convertedInput).build(); - } - - Object finalInput = input; - Assert.notNull(convertedInput, () -> "Failed to convert input: " + finalInput + " to " + type); - return convertedInput; - } - - // TODO temporary fix for https://github.com/spring-cloud/spring-cloud-stream/issues/2178 - // need a cleaner solution - @SuppressWarnings("unchecked") - private Message filterOutHeaders(Message message) { - return MessageBuilder.fromMessage(message).removeHeader("spring.cloud.stream.sendto.destination").build(); - } - - private boolean isExtractPayload(Message message, Type type) { - if (this.propagateInputHeaders) { - return false; - } - if (this.isRoutingFunction()) { - return false; - } - if (FunctionTypeUtils.isCollectionOfMessage(type)) { - return true; - } - if (FunctionTypeUtils.isMessage(type)) { - return false; - } - - Object payload = message.getPayload(); - if ((payload instanceof byte[])) { - return false; - } - if (ObjectUtils.isArray(payload)) { - payload = CollectionUtils.arrayToList(payload); - } - if (payload instanceof Collection && !CollectionUtils.isEmpty((Collection) payload) - && Message.class.isAssignableFrom(CollectionUtils.findCommonElementType((Collection) payload))) { - return true; - } - if (this.containsRetainMessageSignalInHeaders(message)) { - return false; - } - return true; - } - - /** - * This is an optional conversion which would only happen if `expected-content-type` is - * set as a header in a message or explicitly provided as part of the lookup. - */ - @SuppressWarnings("unchecked") - private Object convertOutputIfNecessary(Object output, Type type, String[] contentType) { - Object convertedOutput = output; - if (this.skipOutputConversion) { - return convertedOutput; - } - - if (convertedOutput instanceof Publisher) { - return this.convertOutputPublisherIfNecessary((Publisher) convertedOutput, type, contentType); - } - - if (convertedOutput instanceof Message) { - if (((Message) convertedOutput).getPayload() instanceof byte[] && ObjectUtils.isEmpty(contentType)) { - return convertedOutput; - } - else if (isExtractPayload((Message) convertedOutput, type)) { - convertedOutput = ((Message) convertedOutput).getPayload(); - } - } - - if (this.enhancer != null) { - convertedOutput = enhancer.apply(convertedOutput); - } - if (this.getTarget() instanceof PassThruFunction) { // scst-2303 - Message enrichedMessage; - if (convertedOutput instanceof Message) { - enrichedMessage = MessageBuilder.fromMessage((Message) convertedOutput) - .setHeader(MessageHeaders.CONTENT_TYPE, contentType[0]).build(); - } - else { - enrichedMessage = MessageBuilder.withPayload(convertedOutput) - .setHeader(MessageHeaders.CONTENT_TYPE, contentType[0]).build(); - } - return messageConverter.toMessage(enrichedMessage.getPayload(), enrichedMessage.getHeaders()); - } - - if (ObjectUtils.isEmpty(contentType)) { - return convertedOutput; - } - - - if (FunctionTypeUtils.isMultipleArgumentType(type)) { - convertedOutput = this.convertMultipleOutputArgumentTypeIfNecesary(convertedOutput, type, contentType); - } - else if (convertedOutput instanceof Message) { - convertedOutput = this.convertOutputMessageIfNecessary(convertedOutput, ObjectUtils.isEmpty(contentType) ? null : contentType[0]); - } - else if (convertedOutput instanceof Collection && this.isOutputTypeMessage()) { - convertedOutput = this.convertMultipleOutputValuesIfNecessary(convertedOutput, ObjectUtils.isEmpty(contentType) ? null : contentType); - } - else if (ObjectUtils.isArray(convertedOutput) && !(convertedOutput instanceof byte[])) { - convertedOutput = this.convertMultipleOutputValuesIfNecessary(convertedOutput, ObjectUtils.isEmpty(contentType) ? null : contentType); - } - else { - convertedOutput = messageConverter.toMessage(convertedOutput, - new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType == null ? "application/json" : contentType[0]))); - } - - return convertedOutput; - } - - /** - * Will check if message contains any of the headers that are considered to serve as - * signals to retain output as Message (regardless of the output type of function). - * At this moment presence of 'scf-func-name' header or any header that begins with `lambda' - * (use by AWS) will result in this method returning true. - */ - /* - * TODO we need to investigate if this could be extracted into some type of strategy since at - * the pure core level there is no case for this to ever be true. In fact today it is only AWS Lambda - * case that requires it since it may contain forwarding url - */ - private boolean containsRetainMessageSignalInHeaders(Message message) { - if (functionInvocationHelper != null && functionInvocationHelper.isRetainOuputAsMessage(message)) { - return true; - } - else { - for (String headerName : message.getHeaders().keySet()) { - if (headerName.startsWith("lambda") || - headerName.startsWith("scf-func-name")) { - return true; - } - } - return false; - } - } - - /* - * - */ - private Object convertNonMessageInputIfNecessary(Type inputType, Object input, boolean maybeJson) { - Object convertedInput = null; - Class rawInputType = this.isTypePublisher(inputType) || this.isInputTypeMessage() - ? FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType)) - : this.getRawClassFor(inputType); - - if (maybeJson && !Message.class.isAssignableFrom(rawInputType)) { - if (FunctionTypeUtils.isMessage(inputType)) { - inputType = FunctionTypeUtils.getGenericType(inputType); - } - if (Object.class != inputType) { - convertedInput = SimpleFunctionRegistry.this.jsonMapper.fromJson(input, inputType); - } - } - else if (SimpleFunctionRegistry.this.conversionService != null - && !rawInputType.equals(input.getClass()) - && SimpleFunctionRegistry.this.conversionService.canConvert(input.getClass(), rawInputType)) { - convertedInput = SimpleFunctionRegistry.this.conversionService.convert(input, rawInputType); - } - if (convertedInput == null && logger.isDebugEnabled()) { - logger.debug("Failed to convert input '" + input + "' to type " + inputType + ". Will use it as is."); - } - return convertedInput == null ? input : convertedInput; - } - - /* - * - */ - private boolean isWrapConvertedInputInMessage(Object convertedInput) { - return this.inputType != null - && FunctionTypeUtils.isMessage(this.inputType) - && !(convertedInput instanceof Message) - && !(convertedInput instanceof Publisher) - && !(convertedInput instanceof OriginalMessageHolder); - } - - /* - * - */ - private Type extractActualValueTypeIfNecessary(Type type) { - if (type instanceof ParameterizedType && (FunctionTypeUtils.isPublisher(type) || FunctionTypeUtils.isMessage(type))) { - return FunctionTypeUtils.getGenericType(type); - } - return type; - } - - /* - * - */ - private boolean isConversionHintRequired(Type actualType, Class rawType) { - if (Collection.class.isAssignableFrom(rawType) || Map.class.isAssignableFrom(rawType)) { - return true; - } - return rawType != actualType && !FunctionTypeUtils.isMessage(actualType); - } - - /* - * - */ - private Object convertInputMessageIfNecessary(Message message, Type type) { - if (type == null) { - return null; - } - if (message.getPayload() instanceof Optional) { - return message; - } - if (message.getPayload() instanceof Collection) { - Type itemType = FunctionTypeUtils.getImmediateGenericType(type, 0); - if (itemType == null) { - itemType = type; - } - Type collectionType = CollectionUtils.findCommonElementType((Collection) message.getPayload()); - if (collectionType == itemType) { - return message.getPayload(); - } - } - - Object convertedInput = message.getPayload(); - - Type itemType = this.extractActualValueTypeIfNecessary(type); - Class rawType = FunctionTypeUtils.isMessage(type) - ? FunctionTypeUtils.getRawType(itemType) - : FunctionTypeUtils.getRawType(type); - convertedInput = this.isConversionHintRequired(type, rawType) - ? SimpleFunctionRegistry.this.messageConverter.fromMessage(message, rawType, itemType) - : SimpleFunctionRegistry.this.messageConverter.fromMessage(message, rawType); - - if (convertedInput != null && !rawType.isAssignableFrom(convertedInput.getClass())) { - logger.warn("Failed to convert input to " + rawType + ". Will attempt to invoke function with raw type"); - } - - if (FunctionTypeUtils.isMessage(type)) { - if (convertedInput == null) { - if (logger.isDebugEnabled()) { - /* - * In the event conversion was unsuccessful we simply return the original un-converted message. - * This will help to deal with issues like KafkaNull and others. However if this was not the intention - * of the developer, this would be discovered early in the development process where the - * additional message converter could be added to facilitate the conversion. - */ - logger.debug("Input type conversion of payload " + message.getPayload() + " resulted in 'null'. " - + "Will use the original message as input."); - } - - convertedInput = message; - } - else { - if (!(convertedInput instanceof Message)) { - convertedInput = MessageBuilder.withPayload(convertedInput).copyHeaders(message.getHeaders()).build(); - } - } - } - return convertedInput; - } - - /** - * This method handles function with multiple output arguments (e.g. Tuple2<..>) - */ - private Object convertMultipleOutputArgumentTypeIfNecesary(Object output, Type type, String[] contentType) { - Type[] outputTypes = ((ParameterizedType) type).getActualTypeArguments(); - Object[] multipleValueArguments = this.parseMultipleValueArguments(output, outputTypes.length); - Object[] convertedOutputs = new Object[outputTypes.length]; - for (int i = 0; i < multipleValueArguments.length; i++) { - String[] ctToUse = !ObjectUtils.isEmpty(contentType) - ? new String[]{contentType[i]} - : new String[] {"application/json"}; - Object convertedInput = this.convertOutputIfNecessary(multipleValueArguments[i], outputTypes[i], ctToUse); - convertedOutputs[i] = convertedInput; - } - return Tuples.fromArray(convertedOutputs); - } - - /* - * - */ - @SuppressWarnings("unchecked") - private Object convertOutputMessageIfNecessary(Object output, String expectedOutputContetntType) { - String contentType = ((Message) output).getHeaders().containsKey(FunctionProperties.EXPECT_CONTENT_TYPE_HEADER) - ? (String) ((Message) output).getHeaders().get(FunctionProperties.EXPECT_CONTENT_TYPE_HEADER) - : expectedOutputContetntType; - - if (StringUtils.hasText(contentType)) { - Map headersMap = new HashMap(((Message) output).getHeaders()); - String[] expectedContentTypes = StringUtils.delimitedListToStringArray(contentType, ","); - for (String expectedContentType : expectedContentTypes) { - headersMap.put(MessageHeaders.CONTENT_TYPE, expectedContentType); - Message message = MessageBuilder.withPayload(((Message) output).getPayload()).copyHeaders(headersMap).build(); - Object result = messageConverter.toMessage(message.getPayload(), message.getHeaders()); - if (result != null) { - return result; - } - } - } - return output; - } - - /** - * This one is used to convert individual value of Collection or array. - */ - @SuppressWarnings("unchecked") - private Object convertMultipleOutputValuesIfNecessary(Object output, String[] contentType) { - Collection outputCollection = ObjectUtils.isArray(output) ? CollectionUtils.arrayToList(output) : (Collection) output; - Collection convertedOutputCollection = outputCollection instanceof List ? new ArrayList<>() : new TreeSet<>(); - Type type = this.isOutputTypeMessage() ? FunctionTypeUtils.getGenericType(this.outputType) : this.outputType; - for (Object outToConvert : outputCollection) { - Object result = this.convertOutputIfNecessary(outToConvert, type, contentType); - Assert.notNull(result, () -> "Failed to convert output '" + outToConvert + "'"); - convertedOutputCollection.add(result); - } - return ObjectUtils.isArray(output) ? convertedOutputCollection.toArray() : convertedOutputCollection; - } - - /* - * - */ - @SuppressWarnings("unchecked") - private Object convertInputPublisherIfNecessary(Publisher publisher, Type type) { - if (FunctionTypeUtils.isMono(type) && publisher instanceof Flux) { - publisher = Mono.from(publisher); - } - else if (FunctionTypeUtils.isFlux(type) && publisher instanceof Mono) { - publisher = Flux.from(publisher); - } - Type actualType = type != null && FunctionTypeUtils.isPublisher(type) - ? FunctionTypeUtils.getImmediateGenericType(type, 0) - : type; - return publisher instanceof Mono - ? Mono.from(publisher).map(v -> { - try { - return this.convertInputIfNecessary(v, actualType == null ? type : actualType); - } - catch (Exception e) { - throw new IllegalStateException("Failed to convert input", e); - } - }) - : Flux.from(publisher).map(v -> { - try { - return this.convertInputIfNecessary(v, actualType == null ? type : actualType); - } - catch (Exception e) { - throw new IllegalStateException("Failed to convert input", e); - } - }); - } - - /* - * - */ - @SuppressWarnings("unchecked") - private Object convertOutputPublisherIfNecessary(Publisher publisher, Type type, String[] expectedOutputContentType) { - return publisher instanceof Mono - ? Mono.from(publisher).map(v -> { - try { - return this.convertOutputIfNecessary(v, type, expectedOutputContentType); - } - catch (Exception e) { - throw new IllegalStateException("Failed to convert output", e); - } - }) - : Flux.from(publisher).map(v -> { - try { - return this.convertOutputIfNecessary(v, type, expectedOutputContentType); - } - catch (Exception e) { - throw new IllegalStateException("Failed to convert output", e); - } - }); - } - } - - /** - * - */ - private static final class OriginalMessageHolder { - private final Object value; - - private final Message originalMessage; - - private OriginalMessageHolder(Object value, Message originalMessage) { - this.value = value; - this.originalMessage = originalMessage; - } - - public Object getValue() { - return this.value; - } - - public Message getOriginalMessage() { - return this.originalMessage; - } - } - - public static class PassThruFunction implements Function { - @Override - public Object apply(Object t) { - return t; - } - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java deleted file mode 100644 index 55299fb99..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.Gson; -import io.cloudevents.spring.messaging.CloudEventMessageConverter; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.function.cloudevent.CloudEventsFunctionInvocationHelper; -import org.springframework.cloud.function.context.DefaultMessageRoutingHandler; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.MessageRoutingCallback; -import org.springframework.cloud.function.context.catalog.BeanFactoryAwareFunctionRegistry; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.core.FunctionInvocationHelper; -import org.springframework.cloud.function.json.GsonMapper; -import org.springframework.cloud.function.json.JacksonMapper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.cloud.function.utils.PrimitiveTypesFromStringMessageConverter; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.core.ResolvableType; -import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.core.convert.support.ConfigurableConversionService; -import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.converter.ByteArrayMessageConverter; -import org.springframework.messaging.converter.CompositeMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.converter.StringMessageConverter; -import org.springframework.stereotype.Component; -import org.springframework.util.ClassUtils; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * @author Dave Syer - * @author Mark Fisher - * @author Oleg Zhurakousky - * @author Artem Bilan - * @author Anshul Mehra - * @author Soby Chacko - * @author Chris Bono - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnMissingBean(FunctionCatalog.class) -@EnableConfigurationProperties(FunctionProperties.class) -@AutoConfigureAfter(name = {"org.springframework.cloud.function.deployer.FunctionDeployerConfiguration"}) -public class ContextFunctionCatalogAutoConfiguration { - - /** - * The name of the property to specify desired JSON mapper. Available values are `jackson' and 'gson'. - */ - public static final String JSON_MAPPER_PROPERTY = "spring.cloud.function.preferred-json-mapper"; - - @Bean - public FunctionRegistry functionCatalog(List messageConverters, JsonMapper jsonMapper, - ConfigurableApplicationContext context, @Nullable FunctionInvocationHelper> functionInvocationHelper) { - ConfigurableConversionService conversionService = (ConfigurableConversionService) context.getBeanFactory().getConversionService(); - if (conversionService == null) { - conversionService = new DefaultConversionService(); - } - Map converters = context.getBeansOfType(GenericConverter.class); - for (GenericConverter converter : converters.values()) { - conversionService.addConverter(converter); - } - - SmartCompositeMessageConverter messageConverter = null; - List mcList = new ArrayList<>(); - - if (!CollectionUtils.isEmpty(messageConverters)) { - for (MessageConverter mc : messageConverters) { - if (mc instanceof CompositeMessageConverter) { - List conv = ((CompositeMessageConverter) mc).getConverters().stream().toList(); - mcList.addAll(conv); - } - else { - mcList.add(mc); - } - } - } - - mcList = mcList.stream() - .filter(this::isConverterEligible) - .collect(Collectors.toList()); - - mcList.add(new JsonMessageConverter(jsonMapper)); - mcList.add(new ByteArrayMessageConverter()); - mcList.add(new StringMessageConverter()); - mcList.add(new PrimitiveTypesFromStringMessageConverter(conversionService)); - - messageConverter = new SmartCompositeMessageConverter(mcList); - if (functionInvocationHelper instanceof CloudEventsFunctionInvocationHelper) { - ((CloudEventsFunctionInvocationHelper) functionInvocationHelper).setMessageConverter(messageConverter); - } - FunctionProperties functionProperties = context.getBean(FunctionProperties.class); - return new BeanFactoryAwareFunctionRegistry(conversionService, messageConverter, jsonMapper, functionProperties, functionInvocationHelper); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Bean(RoutingFunction.FUNCTION_NAME) - public RoutingFunction functionRouter(FunctionCatalog functionCatalog, FunctionProperties functionProperties, - BeanFactory beanFactory, @Nullable MessageRoutingCallback routingCallback, - @Nullable DefaultMessageRoutingHandler defaultMessageRoutingHandler) { - if (defaultMessageRoutingHandler != null) { - FunctionRegistration functionRegistration = new FunctionRegistration(defaultMessageRoutingHandler, RoutingFunction.DEFAULT_ROUTE_HANDLER); - functionRegistration.type(FunctionTypeUtils.consumerType(ResolvableType.forClassWithGenerics(Message.class, Object.class).getType())); - ((FunctionRegistry) functionCatalog).register(functionRegistration); - } - return new RoutingFunction(functionCatalog, functionProperties, new BeanFactoryResolver(beanFactory), routingCallback); - } - - private boolean isConverterEligible(Object messageConverter) { - String messageConverterName = messageConverter.getClass().getName(); - if (messageConverterName.startsWith("org.springframework.cloud.")) { - return true; - } - return !messageConverterName.startsWith("org.springframework."); - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(name = "io.cloudevents.spring.messaging.CloudEventMessageConverter") - static class CloudEventsMessageConverterConfiguration { - - @Bean - @ConditionalOnMissingBean - public CloudEventMessageConverter cloudEventMessageConverter() { - return new CloudEventMessageConverter(); - } - } - - @ComponentScan(basePackages = "${spring.cloud.function.scan.packages:functions}", - includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { Supplier.class, Function.class, Consumer.class }), - excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = { Configuration.class, Component.class})) - @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(prefix = "spring.cloud.function.scan", name = "enabled", havingValue = "true", matchIfMissing = true) - protected static class PlainFunctionScanConfiguration { - - } - - @Configuration(proxyBeanMethods = false) - public static class JsonMapperConfiguration { - @Bean - public JsonMapper jsonMapper(ApplicationContext context) { - String preferredMapper = context.getEnvironment().getProperty(JSON_MAPPER_PROPERTY); - if (StringUtils.hasText(preferredMapper)) { - if ("gson".equals(preferredMapper) && ClassUtils.isPresent("com.google.gson.Gson", null)) { - return gson(context); - } - else if ("jackson".equals(preferredMapper) && ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", null)) { - return jackson(context); - } - } - else { - if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", null)) { - return jackson(context); - } - else if (ClassUtils.isPresent("com.google.gson.Gson", null)) { - return gson(context); - } - } - throw new IllegalStateException("Failed to configure JsonMapper. Neither jackson nor gson are present on the claspath"); - } - - private JsonMapper gson(ApplicationContext context) { - Gson gson; - try { - gson = context.getBean(Gson.class); - } - catch (Exception e) { - gson = new Gson(); - } - return new GsonMapper(gson); - } - - private JsonMapper jackson(ApplicationContext context) { - ObjectMapper mapper; - try { - mapper = context.getBean(ObjectMapper.class); - } - catch (Exception e) { - mapper = new ObjectMapper(); - } - return new JacksonMapper(mapper); - } - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java deleted file mode 100644 index b55c28e46..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializer.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.Gson; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.cloud.function.utils.PrimitiveTypesFromStringMessageConverter; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.annotation.AnnotationConfigUtils; -import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.AssignableTypeFilter; -import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.messaging.converter.ByteArrayMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.converter.StringMessageConverter; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - * - */ -public class ContextFunctionCatalogInitializer implements ApplicationContextInitializer { - - /** - * Property name for ignoring pre initilizer. - */ - public static final String IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME = "spring.backgroundpreinitializer.ignore"; - - /** - * Flag for enabling the context function catalog initializer. - */ - public static boolean enabled = true; - - @Override - public void initialize(GenericApplicationContext applicationContext) { - if (enabled - && applicationContext.getEnvironment().getProperty("spring.functional.enabled", Boolean.class, false)) { - ContextFunctionCatalogBeanRegistrar registrar = new ContextFunctionCatalogBeanRegistrar(applicationContext); - applicationContext.addBeanFactoryPostProcessor(registrar); - } - } - - static class ContextFunctionCatalogBeanRegistrar implements BeanDefinitionRegistryPostProcessor { - - private GenericApplicationContext context; - - ContextFunctionCatalogBeanRegistrar(GenericApplicationContext applicationContext) { - this.context = applicationContext; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - } - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { - try { - register(registry, this.context.getDefaultListableBeanFactory()); - } - catch (BeansException e) { - throw e; - } - catch (RuntimeException e) { - throw e; - } - catch (Exception e) { - throw new BeanCreationException("Cannot register from " + getClass(), e); - } - } - - protected void register(BeanDefinitionRegistry registry, ConfigurableListableBeanFactory factory) - throws Exception { - - performPreinitialization(); - - if (this.context.getBeanFactory().getBeanNamesForType(PropertySourcesPlaceholderConfigurer.class, false, - false).length == 0) { - this.context.registerBean(PropertySourcesPlaceholderConfigurer.class, - () -> PropertyPlaceholderAutoConfiguration.propertySourcesPlaceholderConfigurer()); - } - - if (!this.context.getBeanFactory() - .containsBeanDefinition(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { - // Switch off the ConfigurationClassPostProcessor - this.context.registerBean(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME, - DummyProcessor.class, () -> new DummyProcessor()); - // But switch on other annotation processing - AnnotationConfigUtils.registerAnnotationConfigProcessors(this.context); - } - ConfigurationPropertiesBindingPostProcessor.register(registry); - - String preferredMapper = context.getEnvironment().getProperty(ContextFunctionCatalogAutoConfiguration.JSON_MAPPER_PROPERTY); - - if (ClassUtils.isPresent("com.google.gson.Gson", null) && "gson".equals(preferredMapper)) { - if (this.context.getBeanFactory().getBeanNamesForType(Gson.class, false, false).length == 0) { - this.context.registerBean(Gson.class, () -> new Gson()); - } - this.context.registerBean(JsonMapper.class, () -> new ContextFunctionCatalogAutoConfiguration.JsonMapperConfiguration().jsonMapper(this.context)); - } - else if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", null)) { - if (this.context.getBeanFactory().getBeanNamesForType(ObjectMapper.class, false, false).length == 0) { - this.context.registerBean(ObjectMapper.class, () -> new ObjectMapper()); - } - this.context.registerBean(JsonMapper.class, () -> new ContextFunctionCatalogAutoConfiguration.JsonMapperConfiguration().jsonMapper(this.context)); - - } - - String basePackage = this.context.getEnvironment().getProperty("spring.cloud.function.scan.packages", - "functions"); - if (this.context.getEnvironment().getProperty("spring.cloud.function.scan.enabled", Boolean.class, true) - && new ClassPathResource(basePackage.replace(".", "/")).exists()) { - ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.context, false, - this.context.getEnvironment(), this.context); - scanner.addIncludeFilter(new AssignableTypeFilter(Function.class)); - scanner.addIncludeFilter(new AssignableTypeFilter(Supplier.class)); - scanner.addIncludeFilter(new AssignableTypeFilter(Consumer.class)); - for (BeanDefinition bean : scanner.findCandidateComponents(basePackage)) { - String name = bean.getBeanClassName(); - Class type = ClassUtils.resolveClassName(name, this.context.getClassLoader()); - this.context.registerBeanDefinition(name, bean); - this.context.registerBean("registration_" + name, FunctionRegistration.class, - () -> new FunctionRegistration<>(this.context.getBean(name), name).type(type)); - } - } - - if (this.context.getBeanFactory().getBeanNamesForType(FunctionCatalog.class, false, false).length == 0) { - this.context.registerBean(SimpleFunctionRegistry.class, () -> { - List messageConverters = new ArrayList<>(); - JsonMapper jsonMapper = this.context.getBean(JsonMapper.class); - - messageConverters.addAll(context.getBeansOfType(MessageConverter.class).values()); - messageConverters.add(new JsonMessageConverter(jsonMapper)); - messageConverters.add(new ByteArrayMessageConverter()); - messageConverters.add(new StringMessageConverter()); - messageConverters.add(new PrimitiveTypesFromStringMessageConverter(new DefaultConversionService())); - - SmartCompositeMessageConverter messageConverter = new SmartCompositeMessageConverter(messageConverters); - - ConversionService conversionService = new DefaultConversionService(); - return new SimpleFunctionRegistry(conversionService, messageConverter, this.context.getBean(JsonMapper.class)); - }); - this.context.registerBean(FunctionProperties.class, () -> new FunctionProperties()); - this.context.registerBean(FunctionRegistrationPostProcessor.class, - () -> new FunctionRegistrationPostProcessor(this.context.getAutowireCapableBeanFactory() - .getBeanProvider(FunctionRegistration.class))); - } - } - - private void performPreinitialization() { - if (Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)) { - return; - } - try { - Thread thread = new Thread(new Runnable() { - - @Override - public void run() { - runSafely(() -> new DefaultFormattingConversionService()); - } - - public void runSafely(Runnable runnable) { - try { - runnable.run(); - } - catch (Throwable ex) { - // Ignore - } - } - - }, "background-preinit"); - thread.start(); - } - catch (Exception ex) { - } - } - - private class FunctionRegistrationPostProcessor implements BeanPostProcessor { - - @SuppressWarnings("rawtypes") - private final ObjectProvider functions; - - FunctionRegistrationPostProcessor( - @SuppressWarnings("rawtypes") ObjectProvider functions) { - this.functions = functions; - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof FunctionRegistry) { - FunctionRegistry catalog = (FunctionRegistry) bean; - for (FunctionRegistration registration : this.functions) { - Assert.notEmpty(registration.getNames(), - "FunctionRegistration must define at least one name. Was empty"); - if (registration.getType() == null) { - throw new IllegalStateException( - "You need an explicit type for the function: " + registration.getNames()); - // TODO: in principle Spring could know how to extract this - // from the supplier, but in practice there is no functional - // bean registration with parametric types. - } - catalog.register(registration); - } - } - return bean; - } - - } - - } - - /** - * Dummy implementation of a processor. - */ - public static class DummyProcessor { - - public void setMetadataReaderFactory(MetadataReaderFactory obj) { - } - - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java deleted file mode 100644 index 46a501436..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionContextUtils.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; - -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.core.io.Resource; -import org.springframework.core.type.MethodMetadata; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; - -/** - * @author Oleg Zhurakousky - * @since 2.0 - */ -public abstract class FunctionContextUtils { - - public static Type findType(String name, ConfigurableListableBeanFactory registry) { - return findType(registry, name); - } - - public static Type findType(ConfigurableListableBeanFactory registry, String... names) { - AbstractBeanDefinition definition = null; - String actualName = null; - for (String name : names) { - if (registry.containsBeanDefinition(name)) { - definition = (AbstractBeanDefinition) registry.getBeanDefinition(name); - actualName = name; - } - else if (registry.containsBean(name)) { - return FunctionTypeUtils.discoverFunctionTypeFromClass(registry.getBean(name).getClass()); - } - } - - Class beanClass = null; - - - if (definition == null) { - return null; - } - else { - beanClass = resolveBeanClass(definition); - } - - Object source = definition.getSource(); - - Type param = null; - if (source instanceof MethodMetadata) { - param = findBeanType(definition, ((MethodMetadata) source).getDeclaringClassName(), ((MethodMetadata) source).getMethodName()); - } - else if (source instanceof Resource) { - param = registry.getType(actualName); - } - - if (param == null) { - param = definition.getResolvableType().getType(); - } - - if (!(param instanceof ParameterizedType) && beanClass != null) { - return FunctionTypeUtils.discoverFunctionTypeFromClass(beanClass); - } - return param; - } - - public static Class[] getParamTypesFromBeanDefinitionFactory(Class factory, - AbstractBeanDefinition definition) { - if (definition instanceof RootBeanDefinition) { - RootBeanDefinition root = (RootBeanDefinition) definition; - for (Method method : getCandidateMethods(factory, root)) { - if (root.isFactoryMethod(method)) { - return method.getParameterTypes(); - } - } - } - List> params = new ArrayList<>(); - for (ConstructorArgumentValues.ValueHolder holder : definition - .getConstructorArgumentValues().getIndexedArgumentValues().values()) { - params.add(ClassUtils.resolveClassName(holder.getType(), null)); - } - return params.toArray(new Class[0]); - } - - private static Class resolveBeanClass(AbstractBeanDefinition beanDefinition) { - try { - return beanDefinition.hasBeanClass() ? beanDefinition.getBeanClass() : ClassUtils.getDefaultClassLoader().loadClass(beanDefinition.getBeanClassName()); - } - catch (Exception e) { - return null; - } - } - - private static Type findBeanType(AbstractBeanDefinition definition, String declaringClassName, String methodName) { - Class factory = ClassUtils.resolveClassName(declaringClassName, null); - Class[] params = getParamTypesFromBeanDefinitionFactory(factory, definition); - Method method = ReflectionUtils.findMethod(factory, methodName, - params); - Type type = method.getGenericReturnType(); - return type; - } - - private static Method[] getCandidateMethods(final Class factoryClass, - final RootBeanDefinition mbd) { - return (mbd.isNonPublicAccessAllowed() - ? ReflectionUtils.getAllDeclaredMethods(factoryClass) - : factoryClass.getMethods()); - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java deleted file mode 100644 index 861b50c5c..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/FunctionsEndpointAutoConfiguration.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.cloud.function.actuator.FunctionsEndpoint; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @author Oleg Zhurakousky - * @since 3.2 - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass(name = { - "org.springframework.boot.actuate.endpoint.annotation.Endpoint" }) -@ConditionalOnBean(FunctionCatalog.class) -@AutoConfigureAfter(EndpointAutoConfiguration.class) -public class FunctionsEndpointAutoConfiguration { - - @Bean - @ConditionalOnAvailableEndpoint - public FunctionsEndpoint functionsEndpoint(FunctionCatalog functionCatalog) { - return new FunctionsEndpoint(functionCatalog); - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/JsonMessageConverter.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/JsonMessageConverter.java deleted file mode 100644 index 961620119..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/JsonMessageConverter.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; - -import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.core.GenericTypeResolver; -import org.springframework.core.MethodParameter; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.converter.AbstractMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.util.MimeType; -import org.springframework.util.StringUtils; - -/** - * Implementation of {@link MessageConverter} which uses Jackson or Gson libraries to do the - * actual conversion via {@link JsonMapper} instance. - * - * @author Oleg Zhurakousky - * @author Andrey Shlykov - * - * @since 3.0.4 - */ -public class JsonMessageConverter extends AbstractMessageConverter { - - private final JsonMapper jsonMapper; - - public JsonMessageConverter(JsonMapper jsonMapper) { - this(jsonMapper, new MimeType("application", "json"), new MimeType(CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getType(), - CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getSubtype() + "+json")); - } - - public JsonMessageConverter(JsonMapper jsonMapper, MimeType... supportedMimeTypes) { - super(supportedMimeTypes); - this.jsonMapper = jsonMapper; - } - - @Override - protected boolean supports(Class clazz) { - // should not be called, since we override canConvertFrom/canConvertTo instead - throw new UnsupportedOperationException(); - } - - @Override - protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) { - if (!supportsMimeType(headers)) { - return false; - } - return true; - } - - @Override - protected boolean canConvertFrom(Message message, @Nullable Class targetClass) { - return supportsMimeType(message.getHeaders()) && this.canDiscoverConvertToType(message, targetClass); - } - - private boolean canDiscoverConvertToType(Message message, Class targetClass) { - if (targetClass == null || targetClass == Object.class) { - MimeType mimeType = getMimeType(message.getHeaders()); - if (StringUtils.hasText(mimeType.getParameter("type"))) { - return true; - } - return false; - } - return true; - } - - @Override - protected Object convertFromInternal(Message message, Class targetClass, @Nullable Object conversionHint) { - if (conversionHint instanceof ParameterizedTypeReference) { - conversionHint = ((ParameterizedTypeReference) conversionHint).getType(); - } - Type convertToType = this.getResolvedType(targetClass, conversionHint); - if (convertToType == null || convertToType == Object.class) { - MimeType mimeType = getMimeType(message.getHeaders()); - String type = mimeType.getParameter("type"); - if (StringUtils.hasText(type)) { - try { - convertToType = Thread.currentThread().getContextClassLoader().loadClass(type); - } - catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Failed to load class `" + type + "` specified by the provided content-type: " + mimeType, e); - } - } - else { - return message.getPayload(); - } - } - if (targetClass == byte[].class && message.getPayload() instanceof String) { - return ((String) message.getPayload()).getBytes(StandardCharsets.UTF_8); - } - else { - try { - return this.jsonMapper.fromJson(message.getPayload(), convertToType); - } - catch (Exception e) { - if (message.getPayload() instanceof byte[] && String.class.isAssignableFrom(targetClass)) { - return new String((byte[]) message.getPayload(), StandardCharsets.UTF_8); - } - else if (logger.isDebugEnabled()) { - Object payload = message.getPayload(); - if (payload instanceof byte[]) { - payload = new String((byte[]) payload, StandardCharsets.UTF_8); - } - - if (logger.isDebugEnabled()) { - logger.debug("Failed to convert value: " + payload + " to: " + targetClass, e); - } - else { - logger.warn("Failed to convert value: " + payload + " to: " + targetClass); - } - } - } - } - - return null; - } - - @Override - protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers, - @Nullable Object conversionHint) { - return jsonMapper.toJson(payload); - } - - private Type getResolvedType(Class targetClass, @Nullable Object conversionHint) { - if (conversionHint instanceof MethodParameter param) { - param = param.nestedIfOptional(); - if (Message.class.isAssignableFrom(param.getParameterType())) { - param = param.nested(); - } - Type genericParameterType = param.getNestedGenericParameterType(); - Class contextClass = param.getContainingClass(); - return GenericTypeResolver.resolveType(genericParameterType, contextClass); - } - else if (conversionHint instanceof ParameterizedType) { - return (Type) conversionHint; - } - return targetClass; - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java deleted file mode 100644 index 034a6b883..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2012-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.fasterxml.jackson.module.kotlin.KotlinModule; -import kotlin.Unit; -import kotlin.jvm.functions.Function0; -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; -import kotlin.jvm.functions.Function3; -import kotlin.jvm.functions.Function4; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import reactor.core.publisher.Flux; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.ResolvableType; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.util.ObjectUtils; - -/** - * Configuration class which defines the required infrastructure to bootstrap Kotlin - * lambdas as invocable functions within the context of the framework. - * - * @author Oleg Zhurakousky - * @author Adrien Poupard - * @author Dmitriy Tsypov - * @since 2.0 - */ -@Configuration -@ConditionalOnClass(name = "kotlin.jvm.functions.Function0") -public class KotlinLambdaToFunctionAutoConfiguration { - - protected final Log logger = LogFactory.getLog(getClass()); - - @Bean - @ConditionalOnMissingBean - @ConditionalOnClass(name = {"org.springframework.http.converter.json.Jackson2ObjectMapperBuilder", - "com.fasterxml.jackson.module.kotlin.KotlinModule"}) - Jackson2ObjectMapperBuilderCustomizer customizer() { - return new Jackson2ObjectMapperBuilderCustomizer() { - @Override - public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) { - jacksonObjectMapperBuilder.modulesToInstall(KotlinModule.class); - } - }; - } - - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static final class KotlinFunctionWrapper implements Function, Supplier, Consumer, - Function0, Function1, Function2, - Function3, Function4 { - private final Object kotlinLambdaTarget; - - private String name; - - private ConfigurableListableBeanFactory beanFactory; - - public KotlinFunctionWrapper(Object kotlinLambdaTarget) { - this.kotlinLambdaTarget = kotlinLambdaTarget; - } - - @Override - public Object apply(Object input) { - if (ObjectUtils.isEmpty(input)) { - return this.invoke(); - } - else { - return this.invoke(input); - } - } - - @Override - public Object invoke(Object arg0, Object arg1, Object arg2, Object arg3) { - return ((Function4) this.kotlinLambdaTarget).invoke(arg0, arg1, arg2, arg3); - } - - @Override - public Object invoke(Object arg0, Object arg1, Object arg2) { - return ((Function3) this.kotlinLambdaTarget).invoke(arg0, arg1, arg2); - } - - @Override - public Object invoke(Object arg0, Object arg1) { - return ((Function2) this.kotlinLambdaTarget).invoke(arg0, arg1); - } - - @Override - public Object invoke(Object arg0) { - if (CoroutinesUtils.isValidSuspendingFunction(kotlinLambdaTarget, arg0)) { - return CoroutinesUtils.invokeSuspendingFunction(kotlinLambdaTarget, arg0); - } - if (this.kotlinLambdaTarget instanceof Function1) { - return ((Function1) this.kotlinLambdaTarget).invoke(arg0); - } - return ((Function) this.kotlinLambdaTarget).apply(arg0); - } - - @Override - public Object invoke() { - if (CoroutinesUtils.isValidSuspendingSupplier(kotlinLambdaTarget)) { - return CoroutinesUtils.invokeSuspendingSupplier(kotlinLambdaTarget); - } - if (this.kotlinLambdaTarget instanceof Function0) { - return ((Function0) this.kotlinLambdaTarget).invoke(); - } - return ((Supplier) this.kotlinLambdaTarget).get(); - } - - @Override - public void accept(Object input) { - if (CoroutinesUtils.isValidSuspendingFunction(kotlinLambdaTarget, input)) { - CoroutinesUtils.invokeSuspendingConsumer(kotlinLambdaTarget, input); - return; - } - this.apply(input); - } - - @Override - public Object get() { - return this.apply(null); - } - - public FunctionRegistration getFunctionRegistration() { - String name = this.name.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) - ? this.name.replace(FunctionRegistration.REGISTRATION_NAME_SUFFIX, "") - : this.name; - Type functionType = FunctionContextUtils.findType(name, this.beanFactory); - FunctionRegistration registration = new FunctionRegistration<>(this, name); - Type[] types = ((ParameterizedType) functionType).getActualTypeArguments(); - - if (isValidKotlinSupplier(functionType)) { - functionType = ResolvableType.forClassWithGenerics(Supplier.class, ResolvableType.forType(types[0])) - .getType(); - } - else if (isValidKotlinConsumer(functionType, types)) { - functionType = ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forType(types[0])) - .getType(); - } - else if (isValidKotlinFunction(functionType, types)) { - functionType = ResolvableType.forClassWithGenerics(Function.class, ResolvableType.forType(types[0]), - ResolvableType.forType(types[1])).getType(); - } - else if (isValidKotlinSuspendSupplier(functionType, types)) { - Type continuationReturnType = CoroutinesUtils.getSuspendingFunctionReturnType(types[0]); - functionType = ResolvableType.forClassWithGenerics( - Supplier.class, - ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationReturnType)) - ).getType(); - } - else if (isValidKotlinSuspendFunction(functionType, types)) { - Type continuationArgType = CoroutinesUtils.getSuspendingFunctionArgType(types[0]); - Type continuationReturnType = CoroutinesUtils.getSuspendingFunctionReturnType(types[1]); - functionType = ResolvableType.forClassWithGenerics( - Function.class, - ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationArgType)), - ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationReturnType)) - ).getType(); - } - else if (isValidKotlinSuspendConsumer(functionType, types)) { - Type continuationArgType = CoroutinesUtils.getSuspendingFunctionArgType(types[0]); - functionType = ResolvableType.forClassWithGenerics( - Consumer.class, - ResolvableType.forClassWithGenerics(Flux.class, ResolvableType.forType(continuationArgType)) - ).getType(); - } - else if (!FunctionTypeUtils.isFunction(functionType) - && !FunctionTypeUtils.isConsumer(functionType) - && !FunctionTypeUtils.isSupplier(functionType)) { - throw new UnsupportedOperationException("Multi argument Kotlin functions are not currently supported"); - } - registration = registration.type(functionType); - return registration; - } - - private boolean isValidKotlinSupplier(Type functionType) { - return isTypeRepresentedByClass(functionType, Function0.class); - } - - private boolean isValidKotlinConsumer(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function1.class) && - type.length == 2 && - !CoroutinesUtils.isContinuationType(type[0]) && - isTypeRepresentedByClass(type[1], Unit.class); - } - - private boolean isValidKotlinFunction(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function1.class) && - type.length == 2 && - !CoroutinesUtils.isContinuationType(type[0]) && - !isTypeRepresentedByClass(type[1], Unit.class); - } - - private boolean isValidKotlinSuspendSupplier(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function1.class) && - type.length == 2 && - CoroutinesUtils.isContinuationFlowType(type[0]); - } - - private boolean isValidKotlinSuspendConsumer(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function2.class) && - type.length == 3 && - CoroutinesUtils.isFlowType(type[0]) && - CoroutinesUtils.isContinuationUnitType(type[1]); - } - - private boolean isValidKotlinSuspendFunction(Type functionType, Type[] type) { - return isTypeRepresentedByClass(functionType, Function2.class) && - type.length == 3 && - CoroutinesUtils.isContinuationFlowType(type[1]); - } - - private boolean isTypeRepresentedByClass(Type type, Class clazz) { - return type.getTypeName().contains(clazz.getName()); - } - - public Class getObjectType() { - return FunctionRegistration.class; - } - - - public void setName(String name) { - this.name = name; - } - - - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; - } - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java deleted file mode 100644 index 53b134ca9..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.util.Map; -import java.util.function.Function; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.MessageRoutingCallback; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.message.MessageUtils; -import org.springframework.context.expression.MapAccessor; -import org.springframework.expression.BeanResolver; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.DataBindingPropertyAccessor; -import org.springframework.expression.spel.support.SimpleEvaluationContext; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.messaging.Message; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * An implementation of Function which acts as a gateway/router by actually - * delegating incoming invocation to a function specified .. . - * - * @author Oleg Zhurakousky - * @since 2.1 - * - */ -//TODO - perhaps change to Function, Message> -public class RoutingFunction implements Function { - - /** - * The name of this function use by BeanFactory. - */ - public static final String FUNCTION_NAME = "functionRouter"; - - /** - * The name of this function for routing of un-routable messages. - */ - public static final String DEFAULT_ROUTE_HANDLER = "defaultMessageRoutingHandler"; - - private static Log logger = LogFactory.getLog(RoutingFunction.class); - - private final StandardEvaluationContext evalContext = new StandardEvaluationContext(); - - private final SimpleEvaluationContext headerEvalContext = SimpleEvaluationContext - .forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess()).build(); - - private final SpelExpressionParser spelParser = new SpelExpressionParser(); - - private final FunctionCatalog functionCatalog; - - private final FunctionProperties functionProperties; - - private final MessageRoutingCallback routingCallback; - - public RoutingFunction(FunctionCatalog functionCatalog, FunctionProperties functionProperties) { - this(functionCatalog, functionProperties, null, null); - } - - public RoutingFunction(FunctionCatalog functionCatalog, Map propertiesMap, - BeanResolver beanResolver, MessageRoutingCallback routingCallback) { - this(functionCatalog, extractIntoFunctionProperties(propertiesMap), beanResolver, routingCallback); - } - - public RoutingFunction(FunctionCatalog functionCatalog, FunctionProperties functionProperties, - BeanResolver beanResolver, MessageRoutingCallback routingCallback) { - this.functionCatalog = functionCatalog; - this.functionProperties = functionProperties; - this.routingCallback = routingCallback; - this.evalContext.addPropertyAccessor(new MapAccessor()); - evalContext.setBeanResolver(beanResolver); - } - - private static FunctionProperties extractIntoFunctionProperties(Map propertiesMap) { - FunctionProperties functionProperties = new FunctionProperties(); - functionProperties.setDefinition(propertiesMap.get(FunctionProperties.FUNCTION_DEFINITION)); - functionProperties.setRoutingExpression(propertiesMap.get(FunctionProperties.ROUTING_EXPRESSION)); - return functionProperties; - } - - @Override - public Object apply(Object input) { - return this.route(input, input instanceof Publisher); - } - - - /* - * - Check if `this.routingCallback` is present and if it is use it (only for Message input) - * If NOT - * - Check if spring.cloud.function.definition is set in header and if it is use it.(only for Message input) - * If NOT - * - Check if spring.cloud.function.routing-expression is set in header and if it is set use it (only for Message input) - * If NOT - * - Check `spring.cloud.function.definition` is set in FunctionProperties and if it is use it (Message and Publisher) - * If NOT - * - Check `spring.cloud.function.routing-expression` is set in FunctionProperties and if it is use it (Message and Publisher) - * If NOT - * - Fail - */ - private Object route(Object input, boolean originalInputIsPublisher) { - FunctionInvocationWrapper function = null; - - if (input instanceof Message) { - Message message = (Message) input; - if (this.routingCallback != null) { - String functionDefinition = this.routingCallback.routingResult(message); - if (StringUtils.hasText(functionDefinition)) { - function = this.functionFromDefinition(functionDefinition); - } - } - if (function == null) { - if (StringUtils.hasText((String) message.getHeaders().get(FunctionProperties.FUNCTION_DEFINITION))) { - function = functionFromDefinition((String) message.getHeaders().get(FunctionProperties.FUNCTION_DEFINITION)); - if (function.isInputTypePublisher()) { - this.assertOriginalInputIsNotPublisher(originalInputIsPublisher); - } - } - else if (StringUtils.hasText((String) message.getHeaders().get(FunctionProperties.ROUTING_EXPRESSION))) { - function = this.functionFromExpression((String) message.getHeaders().get(FunctionProperties.ROUTING_EXPRESSION), message, true); - if (function.isInputTypePublisher()) { - this.assertOriginalInputIsNotPublisher(originalInputIsPublisher); - } - } - else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { - function = this.functionFromExpression(functionProperties.getRoutingExpression(), message); - } - else if (StringUtils.hasText(functionProperties.getDefinition())) { - function = this.functionFromDefinition(functionProperties.getDefinition()); - } - else { - throw new IllegalStateException("Failed to establish route, since neither were provided: " - + "'spring.cloud.function.definition' as Message header or as application property or " - + "'spring.cloud.function.routing-expression' as application property. Incoming message: " + input); - } - } - } - else if (input instanceof Publisher) { - if (StringUtils.hasText(functionProperties.getDefinition())) { - function = functionFromDefinition(functionProperties.getDefinition()); - } - else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { - function = this.functionFromExpression(functionProperties.getRoutingExpression(), input); - } - else { - return input instanceof Mono - ? Mono.from((Publisher) input).map(v -> route(v, originalInputIsPublisher)) - : Flux.from((Publisher) input).map(v -> route(v, originalInputIsPublisher)); - } - } - else { - this.assertOriginalInputIsNotPublisher(originalInputIsPublisher); - if (StringUtils.hasText(functionProperties.getRoutingExpression())) { - function = this.functionFromExpression(functionProperties.getRoutingExpression(), input); - } - else - if (StringUtils.hasText(functionProperties.getDefinition())) { - function = functionFromDefinition(functionProperties.getDefinition()); - } - else { - throw new IllegalStateException("Failed to establish route, since neither were provided: " - + "'spring.cloud.function.definition' as Message header or as application property or " - + "'spring.cloud.function.routing-expression' as application property."); - } - } - - if (function.getTarget().equals(this)) { - throw new IllegalStateException("Failed to establish route, and routing to itself is not allowed as it creates a loop. Please provide: " - + "'spring.cloud.function.definition' as Message header or as application property or " - + "'spring.cloud.function.routing-expression' as application property."); - } - - return function.apply(input); - } - - private void assertOriginalInputIsNotPublisher(boolean originalInputIsPublisher) { - Assert.isTrue(!originalInputIsPublisher, "Routing input of type Publisher is not supported per individual " - + "values (e.g., message header or POJO). Instead you should use 'spring.cloud.function.definition' or " - + "spring.cloud.function.routing-expression' as application properties."); - } - - private FunctionInvocationWrapper functionFromDefinition(String definition) { - FunctionInvocationWrapper function = this.resolveFunction(definition); - Assert.notNull(function, "Failed to lookup function to route based on the value of 'spring.cloud.function.definition' property '" - + functionProperties.getDefinition() + "'"); - if (logger.isInfoEnabled()) { - logger.info("Resolved function from provided [definition] property " + functionProperties.getDefinition()); - } - return function; - } - - private FunctionInvocationWrapper functionFromExpression(String routingExpression, Object input) { - return functionFromExpression(routingExpression, input, false); - } - - private FunctionInvocationWrapper functionFromExpression(String routingExpression, Object input, boolean isViaHeader) { - Expression expression = spelParser.parseExpression(routingExpression); - if (input instanceof Message) { - input = MessageUtils.toCaseInsensitiveHeadersStructure((Message) input); - } - - String definition = isViaHeader ? expression.getValue(this.headerEvalContext, input, String.class) : expression.getValue(this.evalContext, input, String.class); - Assert.hasText(definition, "Failed to resolve function name based on routing expression '" + functionProperties.getRoutingExpression() + "'"); - FunctionInvocationWrapper function = this.resolveFunction(definition); - Assert.notNull(function, "Failed to lookup function to route to based on the expression '" - + functionProperties.getRoutingExpression() + "' which resolved to '" + definition + "' function definition."); - if (logger.isInfoEnabled()) { - logger.info("Resolved function from provided [routing-expression] " + routingExpression); - } - return function; - } - - private FunctionInvocationWrapper resolveFunction(String definition) { - FunctionInvocationWrapper function = functionCatalog.lookup(definition); - if (function == null) { - function = functionCatalog.lookup(RoutingFunction.DEFAULT_ROUTE_HANDLER); - } - return function; - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/SmartCompositeMessageConverter.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/SmartCompositeMessageConverter.java deleted file mode 100644 index 12c3bdf0a..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/SmartCompositeMessageConverter.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.converter.AbstractMessageConverter; -import org.springframework.messaging.converter.CompositeMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.converter.SmartMessageConverter; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.messaging.support.MessageHeaderAccessor; -import org.springframework.util.MimeType; -import org.springframework.util.StringUtils; - -/** - * - * @author Oleg Zhurakousky - * @author Salvatore Bernardo - * - */ -public class SmartCompositeMessageConverter extends CompositeMessageConverter { - - private Log logger = LogFactory.getLog(this.getClass()); - - public SmartCompositeMessageConverter(Collection converters) { - super(converters); - } - - @Override - @Nullable - public Object fromMessage(Message message, Class targetClass) { - for (MessageConverter converter : getConverters()) { - if (!(message.getPayload() instanceof byte[]) && targetClass.isInstance(message.getPayload()) && !(message.getPayload() instanceof Collection)) { - return message.getPayload(); - } - try { - Object result = converter.fromMessage(message, targetClass); - if (result != null) { - return result; - } - } - catch (Exception e) { - if (logger.isWarnEnabled()) { - logger.warn("Failure during type conversion by " + converter + ". Will try the next converter.", e); - } - } - } - return null; - } - - @SuppressWarnings("unchecked") - @Override - public Object fromMessage(Message message, Class targetClass, @Nullable Object conversionHint) { - if (!(message.getPayload() instanceof byte[]) && targetClass.isInstance(message.getPayload()) && !(message.getPayload() instanceof Collection)) { - return message.getPayload(); - } - Object result = null; - if (message.getPayload() instanceof Iterable && conversionHint != null) { - Iterable iterablePayload = (Iterable) message.getPayload(); - Type genericItemType = FunctionTypeUtils.getImmediateGenericType((Type) conversionHint, 0); - Class genericItemRawType = FunctionTypeUtils.getRawType(genericItemType); - List resultList = new ArrayList<>(); - for (Object item : iterablePayload) { - boolean isConverted = false; - if (item.getClass().getName().startsWith("org.springframework.kafka.support.KafkaNull")) { - resultList.add(null); - isConverted = true; - } - for (Iterator iterator = getConverters().iterator(); iterator.hasNext() && !isConverted;) { - MessageConverter converter = (MessageConverter) iterator.next(); - if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { // TODO Stream stuff, needs to be removed - Message m = MessageBuilder.withPayload(item).copyHeaders(message.getHeaders()).build(); // TODO Message creating may be expensive - Object conversionResult = (converter instanceof SmartMessageConverter & genericItemRawType != genericItemType ? - ((SmartMessageConverter) converter).fromMessage(m, genericItemRawType, genericItemType) : - converter.fromMessage(m, genericItemRawType)); - if (conversionResult != null) { - resultList.add(conversionResult); - isConverted = true; - } - } - } - } - result = resultList; - } - else { - for (MessageConverter converter : getConverters()) { - if (!converter.getClass().getName().endsWith("ApplicationJsonMessageMarshallingConverter")) { // TODO Stream stuff, needs to be removed - result = (converter instanceof SmartMessageConverter ? - ((SmartMessageConverter) converter).fromMessage(message, targetClass, conversionHint) : - converter.fromMessage(message, targetClass)); - if (result != null) { - return result; - } - } - } - } - - return result; - } - - @Override - @Nullable - public Message toMessage(Object payload, @Nullable MessageHeaders headers) { - for (MessageConverter converter : getConverters()) { - if (headers.get(MessageHeaders.CONTENT_TYPE) == null) { - return null; - } - - Object value = headers.get(MessageHeaders.CONTENT_TYPE).toString(); - String[] contentTypes = StringUtils.delimitedListToStringArray((String) value, ","); - for (String contentType : contentTypes) { - if (!MimeType.valueOf(contentType).isConcrete()) { - if (converter instanceof AbstractMessageConverter) { - List supportedMimeTypes = ((AbstractMessageConverter) converter).getSupportedMimeTypes(); - for (MimeType supportedMimeType : supportedMimeTypes) { - if (supportedMimeType.isCompatibleWith(MimeType.valueOf(contentType))) { - MessageHeaderAccessor h = new MessageHeaderAccessor(); - h.copyHeaders(headers); - h.setHeader(MessageHeaders.CONTENT_TYPE, supportedMimeType); - Message result = converter.toMessage(payload, h.getMessageHeaders()); - if (result != null) { - return result; - } - } - } - } - } - else { - MessageHeaderAccessor h = new MessageHeaderAccessor(); - h.copyHeaders(headers); - h.setHeader(MessageHeaders.CONTENT_TYPE, contentType); - Message result = converter.toMessage(payload, h.getMessageHeaders()); - if (result != null) { - return result; - } - } - } - } - return null; - } - - @Override - @Nullable - public Message toMessage(Object payload, @Nullable MessageHeaders headers, @Nullable Object conversionHint) { - for (MessageConverter converter : getConverters()) { - Object value = headers.get(MessageHeaders.CONTENT_TYPE).toString(); - String[] contentTypes = StringUtils.delimitedListToStringArray((String) value, ","); - for (String contentType : contentTypes) { - if (!MimeType.valueOf(contentType).isConcrete()) { - List supportedMimeTypes = ((AbstractMessageConverter) converter).getSupportedMimeTypes(); - for (MimeType supportedMimeType : supportedMimeTypes) { - MessageHeaderAccessor h = new MessageHeaderAccessor(); - h.copyHeaders(headers); - h.setHeader(MessageHeaders.CONTENT_TYPE, supportedMimeType); - Message result = ((SmartMessageConverter) converter).toMessage(payload, h.getMessageHeaders(), conversionHint); - if (result != null) { - return result; - } - } - } - else { - MessageHeaderAccessor h = new MessageHeaderAccessor(); - h.copyHeaders(headers); - h.setHeader(MessageHeaders.CONTENT_TYPE, contentType); - Message result = ((SmartMessageConverter) converter).toMessage(payload, h.getMessageHeaders(), conversionHint); - if (result != null) { - return result; - } - } - } - } - return null; - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/message/MessageUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/message/MessageUtils.java deleted file mode 100644 index 6bfdbf952..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/message/MessageUtils.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.message; - -import java.util.Map; -import java.util.TreeMap; - -import org.springframework.messaging.Message; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public abstract class MessageUtils { - - /** - * Value for 'message-type' typically use as header key. - */ - public static String MESSAGE_TYPE = "message-type"; - - /** - * Value for 'target-protocol' typically use as header key. - */ - public static String TARGET_PROTOCOL = "target-protocol"; - - /** - * Value for 'target-protocol' typically use as header key. - */ - public static String SOURCE_TYPE = "source-type"; - - /** - * Returns (payload, headers) structure identical to `message` while substituting headers with case insensitive map. - */ - public static MessageStructureWithCaseInsensitiveHeaderKeys toCaseInsensitiveHeadersStructure(Message message) { - return new MessageStructureWithCaseInsensitiveHeaderKeys(message); - } - - /** - * !!! INTERNAL USE ONLY, MAY CHANGE OR REMOVED WITHOUT NOTICE!!! - */ - @SuppressWarnings({"rawtypes"}) - public static class MessageStructureWithCaseInsensitiveHeaderKeys { - private final Object payload; - private final Map headers; - - @SuppressWarnings("unchecked") - MessageStructureWithCaseInsensitiveHeaderKeys(Message message) { - this.payload = message.getPayload(); - this.headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - this.headers.putAll(message.getHeaders()); - } - public Object getPayload() { - return payload; - } - - public Map getHeaders() { - return headers; - } - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalSpringBootTest.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalSpringBootTest.java deleted file mode 100644 index 1642146be..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalSpringBootTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.test; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.core.annotation.AliasFor; -import org.springframework.test.context.ContextConfiguration; - -/** - * - * @author Dave Syer - * @since 2.0 - * - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -@SpringBootTest(properties = "spring.functional.enabled=true") -@ContextConfiguration(loader = FunctionalTestContextLoader.class) -public @interface FunctionalSpringBootTest { - - @AliasFor(annotation = SpringBootTest.class, attribute = "properties") - String[] value() default {}; - - @AliasFor(annotation = SpringBootTest.class, attribute = "value") - String[] properties() default {}; - - @AliasFor(annotation = SpringBootTest.class, attribute = "classes") - Class[] classes() default {}; - - @AliasFor(annotation = SpringBootTest.class, attribute = "webEnvironment") - WebEnvironment webEnvironment() default WebEnvironment.MOCK; - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalTestContextLoader.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalTestContextLoader.java deleted file mode 100644 index 8193960e1..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/test/FunctionalTestContextLoader.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.test; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.test.context.SpringBootContextLoader; -import org.springframework.cloud.function.context.FunctionalSpringApplication; -import org.springframework.cloud.function.context.config.ContextFunctionCatalogInitializer; - -/** - * A test context loader for Spring Boot applications that use the - * {@link ContextFunctionCatalogInitializer}. - * - * @author Dave Syer - * @since 2.0 - */ -class FunctionalTestContextLoader extends SpringBootContextLoader { - - @Override - protected SpringApplication getSpringApplication() { - return new FunctionalSpringApplication(); - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/GsonMapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/GsonMapper.java deleted file mode 100644 index 375173104..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/GsonMapper.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.json; - -import java.io.Reader; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; - -import com.google.gson.Gson; -import com.google.gson.JsonElement; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public class GsonMapper extends JsonMapper { - - private final Gson gson; - - public GsonMapper(Gson gson) { - this.gson = gson; - } - - @Override - public String toString(Object value) { - return this.gson.toJson(value); - } - - @Override - protected T doFromJson(Object json, Type type) { - T convertedValue = null; - if (json instanceof byte[]) { - convertedValue = this.gson.fromJson(new String(((byte[]) json), StandardCharsets.UTF_8), type); - } - else if (json instanceof String) { - convertedValue = this.gson.fromJson((String) json, type); - } - else if (json instanceof Reader) { - convertedValue = this.gson.fromJson((Reader) json, type); - } - else if (json instanceof JsonElement) { - convertedValue = this.gson.fromJson((JsonElement) json, type); - } - return convertedValue; - } - - @Override - public byte[] toJson(Object value) { - byte[] jsonBytes = super.toJson(value); - if (jsonBytes == null) { - jsonBytes = this.gson.toJson(value).getBytes(StandardCharsets.UTF_8); - } - return jsonBytes; - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java deleted file mode 100644 index fe501485a..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JacksonMapper.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.json; - -import java.io.Reader; -import java.lang.reflect.Type; -import java.util.Map; -import java.util.function.Consumer; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.type.TypeFactory; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public class JacksonMapper extends JsonMapper { - - private final ObjectMapper mapper; - - public JacksonMapper(ObjectMapper mapper) { - this.mapper = mapper; - } - - public void configureObjectMapper(Consumer configurer) { - configurer.accept(mapper); - } - - @Override - protected T doFromJson(Object json, Type type) { - T convertedValue = null; - JavaType constructType = TypeFactory.defaultInstance().constructType(type); - - try { - if (json instanceof String) { - convertedValue = this.mapper.readValue((String) json, constructType); - } - else if (json instanceof byte[]) { - convertedValue = this.mapper.readValue((byte[]) json, constructType); - } - else if (json instanceof Reader) { - convertedValue = this.mapper.readValue((Reader) json, constructType); - } - else if (json instanceof Map) { - convertedValue = this.mapper.convertValue(json, constructType); - } - } - catch (Exception e) { - throw new IllegalStateException("Failed to convert. Possible bug as the conversion probably shouldn't have been attempted here", e); - } - return convertedValue; - } - - @Override - public byte[] toJson(Object value) { - byte[] jsonBytes = super.toJson(value); - if (jsonBytes == null) { - try { - jsonBytes = this.mapper.writeValueAsBytes(value); - } - catch (Exception e) { - //ignore and let other converters have a chance - } - } - return jsonBytes; - } - - @Override - public String toString(Object value) { - try { - return this.mapper.writeValueAsString(value); - } - catch (JsonProcessingException e) { - throw new IllegalArgumentException("Cannot convert to JSON", e); - } - } - - - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java deleted file mode 100644 index 8da030093..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/json/JsonMapper.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.json; - -import java.io.Reader; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public abstract class JsonMapper { - - private static Log logger = LogFactory.getLog(JsonMapper.class); - - @SuppressWarnings("unchecked") - public T fromJson(Object json, Type type) { - if (json instanceof Collection) { - if (FunctionTypeUtils.isTypeCollection(type)) { - return (T) json; - } - Collection inputs = (Collection) json; - Type itemType = FunctionTypeUtils.getImmediateGenericType(type, 0); - Collection results = FunctionTypeUtils.getRawType(type).isAssignableFrom(List.class) - ? new ArrayList<>() - : new HashSet<>(); - for (Object input : inputs) { - results.add(this.doFromJson(input, itemType)); - } - return (T) results; - } - else { - if (!(json instanceof String) && !(json instanceof byte[]) && !(json instanceof Reader)) { - json = this.toJson(json); - if (FunctionTypeUtils.getRawType(type) == String.class) { - return (T) new String((byte[]) json, StandardCharsets.UTF_8); - } - else if (FunctionTypeUtils.getRawType(type) == byte[].class) { - return (T) json; - } - } - if (json instanceof String && (String.class == type || byte[].class == type)) { - return String.class == type ? (T) json : (T) ((String) json).getBytes(StandardCharsets.UTF_8); - } - else { - return this.doFromJson(json, type); - } - } - } - - protected abstract T doFromJson(Object json, Type type); - - public byte[] toJson(Object value) { - byte[] result = null; - if (isJsonString(value)) { - if (logger.isDebugEnabled()) { - logger.debug( - "String already represents JSON. Skipping conversion in favor of 'getBytes(StandardCharsets.UTF_8'."); - } - result = value instanceof byte[] ? (byte[]) value : ((String) value).getBytes(StandardCharsets.UTF_8); - } - else if (value instanceof byte[]) { - result = (byte[]) value; - } - return result; - } - - public abstract String toString(Object value); - - /** - * Performs a simple validation on an object to see if it appears to be a JSON string. - * NOTE: the validation is very rudimentary and simply checks that the object is a String and begins - * and ends with matching pairs of "{}" or "[]" or "\"\"" and therefore may not handle some corner cases. - * Primarily intended for internal of the framework. - * @param value candidate object to evaluate - * @return true if and object appears to be a valid JSON string, otherwise false. - */ - public static boolean isJsonString(Object value) { - boolean isJson = false; - if (value instanceof byte[]) { - value = new String((byte[]) value, StandardCharsets.UTF_8); - } - if (value instanceof String) { - String str = ((String) value).trim(); - isJson = (str.startsWith("\"") && str.endsWith("\"")) || - (str.startsWith("{") && str.endsWith("}")) || - (str.startsWith("[") && str.endsWith("]")); - } - - return isJson; - } - - public static boolean isJsonStringRepresentsCollection(Object value) { - boolean isJson = false; - if (value instanceof Collection && !value.getClass().getPackage().getName().startsWith("reactor.util.function")) { - return true; - } - if (value instanceof byte[]) { - value = new String((byte[]) value, StandardCharsets.UTF_8); - } - if (value instanceof String) { - String str = ((String) value).trim(); - isJson = isJsonString(value) && str.startsWith("[") && str.endsWith("]"); - } - return isJson; - } - - public static boolean isJsonStringRepresentsMap(Object value) { - boolean isJson = false; - if (value instanceof byte[]) { - value = new String((byte[]) value, StandardCharsets.UTF_8); - } - if (value instanceof String) { - String str = ((String) value).trim(); - isJson = isJsonString(value) && str.startsWith("{") && str.endsWith("}"); - } - return isJson; - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/DefaultFunctionObservationConvention.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/DefaultFunctionObservationConvention.java deleted file mode 100644 index a31e8719b..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/DefaultFunctionObservationConvention.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2013-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.observability; - -import io.micrometer.common.KeyValues; - -/** - * Default implementation of {@link FunctionReceiverObservationConvention}. - * - * @author Marcin Grzejszczak - * @since 4.0.0 - */ -public class DefaultFunctionObservationConvention implements FunctionObservationConvention { - - /** - * Singleton instance of this convention. - */ - public static final FunctionObservationConvention INSTANCE = new DefaultFunctionObservationConvention(); - - @Override - public KeyValues getLowCardinalityKeyValues(FunctionContext context) { - return KeyValues.of(FunctionObservation.FunctionLowCardinalityTags.FUNCTION_NAME.withValue(context.getTargetFunction().getFunctionDefinition())); - } - - @Override - public String getName() { - return "spring.cloud.function"; - } - - @Override - public String getContextualName(FunctionContext context) { - return context.getTargetFunction().getFunctionDefinition() + " process"; - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionContext.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionContext.java deleted file mode 100644 index e5bdd89be..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionContext.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2013-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.observability; - -import io.micrometer.observation.Observation; - -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry; -import org.springframework.messaging.Message; - -/** - * {@link Observation.Context} for function processing. - * - * @author Marcin Grzejszczak - * @author Oleg Zhurakousky - * @since 4.0.0 - */ -public class FunctionContext extends Observation.Context { - - private final SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction; - - private final Message message; - - public FunctionContext(SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction, Message message) { - this.targetFunction = targetFunction; - this.message = message; - } - - public SimpleFunctionRegistry.FunctionInvocationWrapper getTargetFunction() { - return targetFunction; - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservation.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservation.java deleted file mode 100644 index a50b38814..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservation.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2013-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.observability; - -import io.micrometer.common.docs.KeyName; -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationConvention; -import io.micrometer.observation.docs.ObservationDocumentation; - -/** - * @author Marcin Grzejszczak - * @author Oleg Zhurakousky - * @since 4.0.0 - */ -enum FunctionObservation implements ObservationDocumentation { - - /** - * Observation created around processing a message (functional bean processing). - */ - FUNCTION_PROCESSING_OBSERVATION { - @Override - public Class> getDefaultConvention() { - return DefaultFunctionObservationConvention.class; - } - - @Override - public KeyName[] getLowCardinalityKeyNames() { - return FunctionLowCardinalityTags.values(); - } - - @Override - public String getPrefix() { - return "spring.cloud.function"; - } - }; - - enum FunctionLowCardinalityTags implements KeyName { - - /** - * Name of the function. - */ - FUNCTION_NAME { - public String asString() { - return "spring.cloud.function.definition"; - } - } - - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservationConvention.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservationConvention.java deleted file mode 100644 index 1a5be2a0c..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/FunctionObservationConvention.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.observability; - -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationConvention; - -/** - * {@link ObservationConvention} for {@link FunctionContext}. - * - * @author Marcin Grzejszczak - * @author Oleg Zhurakousky - * @since 4.0.0 - */ -public interface FunctionObservationConvention extends ObservationConvention { - - @Override - default boolean supportsContext(Observation.Context context) { - return context instanceof FunctionContext; - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationAutoConfiguration.java deleted file mode 100644 index ec0e1650f..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationAutoConfiguration.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.observability; - -import io.micrometer.observation.ObservationRegistry; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.cloud.function.context.catalog.FunctionAroundWrapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @author Oleg Zhurakousky - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnBean(org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration.class) -public class ObservationAutoConfiguration { - - @Bean - @ConditionalOnMissingBean - @ConditionalOnBean(ObservationRegistry.class) - public FunctionAroundWrapper observationFunctionAroundWrapper(ObservationRegistry registry, - ObjectProvider functionObservationConvention) { - return new ObservationFunctionAroundWrapper(registry, - functionObservationConvention.getIfAvailable(() -> null)); - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationFunctionAroundWrapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationFunctionAroundWrapper.java deleted file mode 100644 index 8f62c5fa8..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/observability/ObservationFunctionAroundWrapper.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.observability; - -import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationRegistry; - -import org.springframework.cloud.function.context.catalog.FunctionAroundWrapper; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; - - -/** - * @author Marcin Grzejszczak - * @author Oleg Zhurakousky - * @since 4.0.0 - */ -public class ObservationFunctionAroundWrapper extends FunctionAroundWrapper { - private final ObservationRegistry observationRegistry; - - private final FunctionObservationConvention functionObservationConvention; - - public ObservationFunctionAroundWrapper(ObservationRegistry observationRegistry, @Nullable FunctionObservationConvention functionObservationConvention) { - this.observationRegistry = observationRegistry; - this.functionObservationConvention = functionObservationConvention; - } - - @Override - protected Object doApply(Object message, SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction) { - return nonReactorStream((Message) message, targetFunction); - } - - private Object nonReactorStream(Message message, - SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction) { - return functionProcessingObservation(targetFunction, message).observe(() -> targetFunction.apply(message)); - } - - private Observation functionProcessingObservation(SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction, Message message) { - return FunctionObservation.FUNCTION_PROCESSING_OBSERVATION.observation(this.functionObservationConvention, DefaultFunctionObservationConvention.INSTANCE, () -> new FunctionContext(targetFunction, message), this.observationRegistry); - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java deleted file mode 100644 index 1e13b1a33..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionClassUtils.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.utils; - -import java.io.InputStream; -import java.net.URL; -import java.util.Collections; -import java.util.List; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.core.KotlinDetector; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; - -/** - * General utility class which aggregates various class-level utility functions - * used by the framework. - * - * @author Oleg Zhurakousky - * @since 3.0.1 - */ -public final class FunctionClassUtils { - - private static Log logger = LogFactory.getLog(FunctionClassUtils.class); - - private FunctionClassUtils() { - - } - - /** - * Discovers the start class in the currently running application. - * The discover search order is 'MAIN_CLASS' environment property, - * 'MAIN_CLASS' system property, META-INF/MANIFEST.MF:'Start-Class' attribute, - * meta-inf/manifest.mf:'Start-Class' attribute. - * - * @return instance of Class which represent the start class of the application. - */ - public static Class getStartClass() { - ClassLoader classLoader = FunctionClassUtils.class.getClassLoader(); - return getStartClass(classLoader); - } - - static Class getStartClass(ClassLoader classLoader) { - Class mainClass = null; - if (System.getenv("MAIN_CLASS") != null) { - mainClass = ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader); - } - else if (System.getProperty("MAIN_CLASS") != null) { - mainClass = ClassUtils.resolveClassName(System.getProperty("MAIN_CLASS"), classLoader); - } - else { - try { - Class result = getStartClass( - Collections.list(classLoader.getResources(JarFile.MANIFEST_NAME)), classLoader); - if (result == null) { - result = getStartClass(Collections - .list(classLoader.getResources("meta-inf/manifest.mf")), classLoader); - } - Assert.notNull(result, "Failed to locate main class"); - mainClass = result; - } - catch (Exception ex) { - throw new IllegalStateException("Failed to discover main class. An attempt was made to discover " - + "main class as 'MAIN_CLASS' environment variable, system property as well as " - + "entry in META-INF/MANIFEST.MF (in that order).", ex); - } - } - logger.info("Main class: " + mainClass); - return mainClass; - } - - private static Class getStartClass(List list, ClassLoader classLoader) { - if (logger.isTraceEnabled()) { - logger.trace("Searching manifests: " + list); - } - for (URL url : list) { - try { - InputStream inputStream = null; - Manifest manifest = new Manifest(url.openStream()); - logger.info("Searching for start class in manifest: " + url); - if (logger.isDebugEnabled()) { - manifest.write(System.out); - } - try { - String startClassName = manifest.getMainAttributes().getValue("Start-Class"); - if (!StringUtils.hasText(startClassName)) { - startClassName = manifest.getMainAttributes().getValue("Main-Class"); - } - - if (StringUtils.hasText(startClassName)) { - Class startClass = ClassUtils.forName(startClassName, classLoader); - - if (KotlinDetector.isKotlinType(startClass)) { - PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver(classLoader); - String packageName = startClass.getPackage().getName(); - Resource[] resources = r.getResources("classpath:" + packageName.replace(".", "/") + "/*.class"); - for (int i = 0; i < resources.length; i++) { - Resource resource = resources[i]; - String className = packageName + "." + (resource.getFilename().replace("/", ".")).replace(".class", ""); - startClass = ClassUtils.forName(className, classLoader); - if (isSpringBootApplication(startClass)) { - logger.info("Loaded Main Kotlin Class: " + startClass); - return startClass; - } - } - } - else if (isSpringBootApplication(startClass)) { - logger.info("Loaded Start Class: " + startClass); - return startClass; - } - } - } - finally { - if (inputStream != null) { - inputStream.close(); - } - } - } - catch (Exception ex) { - logger.debug("Failed to determine Start-Class in manifest file of " + url, ex); - } - } - return null; - } - - private static boolean isSpringBootApplication(Class startClass) { - return startClass.getDeclaredAnnotation(SpringBootApplication.class) != null - || startClass.getDeclaredAnnotation(SpringBootConfiguration.class) != null; - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionMessageUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionMessageUtils.java deleted file mode 100644 index 76a6974c0..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionMessageUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.springframework.cloud.function.utils; - -import org.springframework.cloud.function.context.message.MessageUtils; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; - -/** - * - * !!! INTERNAL ONLY !!! - * - * @author Oleg Zhurakousky - * - */ -public final class FunctionMessageUtils { - - private FunctionMessageUtils() { - - } - - public static String getSourceType(String functionDefinition, Message message) { - return determineSourceFromHeaders(message.getHeaders()); - } - - public static String getTargetType(String functionDefinition, Message message) { - return message.getHeaders().containsKey(MessageUtils.TARGET_PROTOCOL) ? (String) message.getHeaders().get(MessageUtils.TARGET_PROTOCOL) : "unknown"; - } - - private static String determineSourceFromHeaders(MessageHeaders headers) { - for (String key : headers.keySet()) { - if (key.equals(MessageUtils.SOURCE_TYPE)) { - return (String) headers.get(MessageUtils.SOURCE_TYPE); - } - else if (key.startsWith("amqp_")) { - return "amqp"; - } - else if (key.startsWith("kafka_")) { - return "kafka"; - } - else if (key.startsWith("aws_")) { - return "aws"; - } - else if (key.startsWith("solace_")) { - return "solace"; - } - else if (key.toLowerCase().equals("user-agent") || key.toLowerCase().equals("accept-encoding") || key.toLowerCase().equals("host")) { - return "http"; - } - // add rsocket - } - return "origin"; - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/PrimitiveTypesFromStringMessageConverter.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/PrimitiveTypesFromStringMessageConverter.java deleted file mode 100644 index adac52099..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/PrimitiveTypesFromStringMessageConverter.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.utils; - -import java.nio.charset.StandardCharsets; - -import org.springframework.core.convert.ConversionService; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.converter.AbstractMessageConverter; -import org.springframework.util.MimeType; - -/** - * - * @author Oleg Zhurakousky - * @since 3.1 - */ -public class PrimitiveTypesFromStringMessageConverter extends AbstractMessageConverter { - - - private final ConversionService conversionService; - - public PrimitiveTypesFromStringMessageConverter(ConversionService conversionService) { - super(new MimeType("text", "plain")); - this.conversionService = conversionService; - } - - - @Override - protected boolean supports(Class clazz) { - return (Integer.class == clazz || Long.class == clazz); - } - - @Override - protected Object convertFromInternal(Message message, Class targetClass, @Nullable Object conversionHint) { - return conversionService.convert(message.getPayload(), targetClass); - } - - @Override - @Nullable - protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers, @Nullable Object conversionHint) { - return payload.toString().getBytes(StandardCharsets.UTF_8); - } -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/SocketUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/SocketUtils.java deleted file mode 100644 index f83beb913..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/SocketUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.utils; - -import java.net.ServerSocket; - -public final class SocketUtils { - - private SocketUtils() { - } - - public static int findAvailableTcpPort() { - try { - ServerSocket s = new ServerSocket(0); - int port = s.getLocalPort(); - s.close(); - return port; - } - catch (Exception e) { - // ignore - } - return 0; - } -} diff --git a/spring-cloud-function-context/src/main/kotlin/org/springframework/cloud/function/context/config/CoroutinesUtils.kt b/spring-cloud-function-context/src/main/kotlin/org/springframework/cloud/function/context/config/CoroutinesUtils.kt deleted file mode 100644 index 8614b69db..000000000 --- a/spring-cloud-function-context/src/main/kotlin/org/springframework/cloud/function/context/config/CoroutinesUtils.kt +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:JvmName("CoroutinesUtils") -package org.springframework.cloud.function.context.config - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.reactive.asFlow -import kotlinx.coroutines.reactor.asFlux -import kotlinx.coroutines.reactor.mono -import reactor.core.publisher.Flux -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type -import java.lang.reflect.WildcardType -import kotlin.coroutines.Continuation -import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn - -/** - * @author Adrien Poupard - * - */ - -fun getSuspendingFunctionArgType(type: Type): Type { - return getFlowTypeArguments(type) -} - -fun getFlowTypeArguments(type: Type): Type { - if(!isFlowType(type)) { - return type - } - val parameterizedLowerType = type as ParameterizedType - if(parameterizedLowerType.actualTypeArguments.isEmpty()) { - return parameterizedLowerType - } - - val actualTypeArgument = parameterizedLowerType.actualTypeArguments[0] - return if(actualTypeArgument is WildcardType) { - val wildcardTypeLower = parameterizedLowerType.actualTypeArguments[0] as WildcardType - wildcardTypeLower.upperBounds[0] - } else { - actualTypeArgument - } -} - -fun isFlowType(type: Type): Boolean { - return type.typeName.startsWith(Flow::class.qualifiedName!!) -} - -fun getSuspendingFunctionReturnType(type: Type): Type { - val lower = getContinuationTypeArguments(type) - return getFlowTypeArguments(lower) -} - -fun isContinuationType(type: Type): Boolean { - return type.typeName.startsWith(Continuation::class.qualifiedName!!) -} - -fun isContinuationUnitType(type: Type): Boolean { - return isContinuationType(type) && type.typeName.contains(Unit::class.qualifiedName!!) -} - -fun isContinuationFlowType(type: Type): Boolean { - return isContinuationType(type) && type.typeName.contains(Flow::class.qualifiedName!!) -} - -private fun getContinuationTypeArguments(type: Type): Type { - if(!isContinuationType(type)) { - return type - } - val parameterizedType = type as ParameterizedType - val wildcardType = parameterizedType.actualTypeArguments[0] as WildcardType - return wildcardType.lowerBounds[0] -} - -fun invokeSuspendingFunction(kotlinLambdaTarget: Any, arg0: Any): Flux { - val function = kotlinLambdaTarget as SuspendFunction - val flux = arg0 as Flux - return mono(Dispatchers.Unconfined) { - suspendCoroutineUninterceptedOrReturn> { - function.invoke(flux.asFlow(), it) - } - }.flatMapMany { - it.asFlux() - } -} - -fun invokeSuspendingSupplier(kotlinLambdaTarget: Any): Flux { - val supplier = kotlinLambdaTarget as SuspendSupplier - return mono(Dispatchers.Unconfined) { - suspendCoroutineUninterceptedOrReturn> { - supplier.invoke(it) - } - }.flatMapMany { - it.asFlux() - } -} - -fun invokeSuspendingConsumer(kotlinLambdaTarget: Any, arg0: Any) { - val consumer = kotlinLambdaTarget as SuspendConsumer - val flux = arg0 as Flux - mono(Dispatchers.Unconfined) { - suspendCoroutineUninterceptedOrReturn { - consumer.invoke(flux.asFlow(), it) - } - }.subscribe() -} - -fun isValidSuspendingFunction(kotlinLambdaTarget: Any, arg0: Any): Boolean { - return arg0 is Flux<*> && kotlinLambdaTarget is Function2<*, *, *> -} - -fun isValidSuspendingSupplier(kotlinLambdaTarget: Any): Boolean { - return kotlinLambdaTarget is Function1<*, *> -} - -private typealias SuspendFunction = (Any?, Any?) -> Any? -private typealias SuspendConsumer = (Any?, Any?) -> Unit? -private typealias SuspendSupplier = (Any?) -> Any? diff --git a/spring-cloud-function-context/src/main/resources/META-INF/spring-configuration-metadata.json b/spring-cloud-function-context/src/main/resources/META-INF/spring-configuration-metadata.json deleted file mode 100644 index 18830d28d..000000000 --- a/spring-cloud-function-context/src/main/resources/META-INF/spring-configuration-metadata.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "hints": [], - "groups": [], - "properties": [ - { - "name": "spring.cloud.function.scan.packages", - "type": "java.lang.String", - "description": "Triggers scanning within the specified base packages for any class that is assignable to java.util.function.Function. For each detected Function class, a bean instance will be added to the context.", - "defaultValue": "functions" - }, - { - "name": "spring.cloud.function.definition", - "type": "java.lang.String", - "description": "Name (e.g., 'foo') or composition instruction (e.g., 'foo|bar') used to resolve default function especially for cases where there is more than one function available in catalog.", - "defaultValue": "" - }, - { - "name": "spring.cloud.function.routing.enabled", - "type": "java.lang.Boolean", - "description": "Enables RoutingFunction which delegates incoming request to a function named via function.name header", - "defaultValue": false - } - ] -} diff --git a/spring-cloud-function-context/src/main/resources/META-INF/spring.factories b/spring-cloud-function-context/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 98a7ced06..000000000 --- a/spring-cloud-function-context/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,4 +0,0 @@ -org.springframework.cloud.function.context.WrapperDetector=\ -org.springframework.cloud.function.context.config.FluxWrapperDetector -org.springframework.context.ApplicationContextInitializer=\ -org.springframework.cloud.function.context.config.ContextFunctionCatalogInitializer diff --git a/spring-cloud-function-context/src/main/resources/META-INF/spring/aot.factories b/spring-cloud-function-context/src/main/resources/META-INF/spring/aot.factories deleted file mode 100644 index 750d9d74c..000000000 --- a/spring-cloud-function-context/src/main/resources/META-INF/spring/aot.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=org.springframework.cloud.function.context.FunctionTypeProcessor \ No newline at end of file diff --git a/spring-cloud-function-context/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-function-context/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 90873e09a..000000000 --- a/spring-cloud-function-context/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,5 +0,0 @@ -org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration -org.springframework.cloud.function.cloudevent.CloudEventsFunctionExtensionConfiguration -org.springframework.cloud.function.context.config.KotlinLambdaToFunctionAutoConfiguration -org.springframework.cloud.function.context.config.FunctionsEndpointAutoConfiguration -org.springframework.cloud.function.observability.ObservationAutoConfiguration diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java deleted file mode 100644 index 0ad602b4e..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventFunctionTests.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.cloudevent; - -import java.net.URI; -import java.text.SimpleDateFormat; -import java.util.UUID; -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.message.MessageUtils; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; - - - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class CloudEventFunctionTests { - - @SuppressWarnings("unchecked") - @Test - public void testBinaryPojoToPojoDefaultOutputHeaderProvider() { - Function function = this.lookup("echo", TestConfiguration.class); - - String id = UUID.randomUUID().toString(); - - Message inputMessage = CloudEventMessageBuilder - .withData("{\"name\":\"Ricky\"}") - .setId(id) - .setSource("https://spring.io/") - .setType("org.springframework") - .build(); - - assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isTrue(); - - Message resultMessage = (Message) function.apply(inputMessage); - - - /* - * Validates that although user only deals with POJO, the framework recognizes - * both on input and output that it is dealing with Cloud Event and generates - * appropriate headers/attributes - */ - assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); - assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(Person.class.getName()); - assertThat(CloudEventMessageUtils.getSource(resultMessage)).isEqualTo(URI.create("http://spring.io/")); - } - - /* - * Aside from the properly processing and recognizing CE, the following tow tests (imperative and reactive) - * also emulate message coming from one protocol going to another via MessageUtils.TARGET_PROTOCOL header that - * is set here explicitly but for instance in s-c-stream is set by the framework - */ - @Test - public void testBinaryPojoToPojoDefaultOutputHeaderProviderImperative() { - Function function = this.lookup("springRelease", TestConfiguration.class); - - String id = UUID.randomUUID().toString(); - - String payload = "{\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }"; - - Message inputMessage = CloudEventMessageBuilder - .withData(payload) - .setId(id) - .setSource("https://spring.io/") - .setType("org.springframework") - .setHeader(MessageUtils.TARGET_PROTOCOL, CloudEventMessageUtils.Protocols.KAFKA) - .build(CloudEventMessageUtils.AMQP_ATTR_PREFIX); - - assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isTrue(); - - Message message = (Message) function.apply(inputMessage); - - /* - * Validates that although user only deals with POJO, the framework recognizes - * both on input and output that it is dealing with Cloud Event and generates - * appropriate headers/attributes - */ - - assertThat(CloudEventMessageUtils.isCloudEvent(message)).isTrue(); - assertThat(CloudEventMessageUtils.getType(message)).isEqualTo(SpringReleaseEvent.class.getName()); - assertThat(CloudEventMessageUtils.getSource(message)).isEqualTo(URI.create("http://spring.io/")); - assertThat(message.getHeaders().get("ce_source")).isEqualTo(URI.create("http://spring.io/")); - } - - @SuppressWarnings("unchecked") - @Test - public void testBinaryPojoToPojoDefaultOutputHeaderProviderReactive() { - Function function = this.lookup("springReleaseReactive", TestConfiguration.class); - - String id = UUID.randomUUID().toString(); - - String payload = "{\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }"; - - Message inputMessage = CloudEventMessageBuilder - .withData(payload) - .setId(id) - .setSource("https://spring.io/") - .setType("org.springframework") - .setHeader(MessageUtils.TARGET_PROTOCOL, CloudEventMessageUtils.Protocols.KAFKA) - .build(CloudEventMessageUtils.AMQP_ATTR_PREFIX); - - assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isTrue(); - - Message message = ((Flux>) function.apply(Flux.just(inputMessage))).blockFirst(); - - /* - * Validates that although user only deals with POJO, the framework recognizes - * both on input and output that it is dealing with Cloud Event and generates - * appropriate headers/attributes - */ - - assertThat(CloudEventMessageUtils.isCloudEvent(message)).isTrue(); - assertThat(CloudEventMessageUtils.getType(message)).isEqualTo(SpringReleaseEvent.class.getName()); - assertThat(CloudEventMessageUtils.getSource(message)).isEqualTo(URI.create("http://spring.io/")); - assertThat(message.getHeaders().get("ce_source")).isEqualTo(URI.create("http://spring.io/")); - } - - @SuppressWarnings("unchecked") - @Test - public void testBinaryPojoToPojoDefaultOutputHeaderProviderReactiveMono() { - Function function = this.lookup("springReleaseReactiveMono", TestConfiguration.class); - - String id = UUID.randomUUID().toString(); - - String payload = "{\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }"; - - Message inputMessage = CloudEventMessageBuilder - .withData(payload) - .setId(id) - .setSource("https://spring.io/") - .setType("org.springframework") - .setHeader(MessageUtils.TARGET_PROTOCOL, CloudEventMessageUtils.Protocols.KAFKA) - .build(CloudEventMessageUtils.AMQP_ATTR_PREFIX); - - assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isTrue(); - - Message message = ((Mono>) function.apply(Mono.just(inputMessage))).block(); - - /* - * Validates that although user only deals with POJO, the framework recognizes - * both on input and output that it is dealing with Cloud Event and generates - * appropriate headers/attributes - */ - - assertThat(CloudEventMessageUtils.isCloudEvent(message)).isTrue(); - assertThat(CloudEventMessageUtils.getType(message)).isEqualTo(SpringReleaseEvent.class.getName()); - assertThat(CloudEventMessageUtils.getSource(message)).isEqualTo(URI.create("http://spring.io/")); - assertThat(message.getHeaders().get("ce_source")).isEqualTo(URI.create("http://spring.io/")); - } - - - // this kind of emulates that message came from Kafka - @SuppressWarnings("unchecked") - @Test - public void testBinaryPojoToPojoDefaultOutputHeaderProviderWithPrefix() { - Function function = this.lookup("echo", TestConfiguration.class); - - String id = UUID.randomUUID().toString(); - - Message inputMessage = CloudEventMessageBuilder - .withData("{\"name\":\"Ricky\"}") - .setHeader("ce_id", id) - .setHeader("ce_source", "https://spring.io/") - .setHeader("ce_type", "org.springframework") - .build(); - - Message resultMessage = (Message) function.apply(inputMessage); - - /* - * Validates that although user only deals with POJO, the framework recognizes - * both on input and output that it is dealing with Cloud Event and generates - * appropriate headers/attributes - */ - assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); - assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(Person.class.getName()); - assertThat(CloudEventMessageUtils.getSource(resultMessage)).isEqualTo(URI.create("http://spring.io/")); - } - - @SuppressWarnings("unchecked") - @Test - public void testStructuredPojoToPojoDefaultOutputAttributeProvider() throws Exception { - String payload = "{\n" + - " \"specversion\" : \"1.0\",\n" + - " \"type\" : \"org.springframework\",\n" + - " \"source\" : \"https://spring.io/\",\n" + - " \"id\" : \"A234-1234-1234\",\n" + - " \"datacontenttype\" : \"application/json\",\n" + - " \"data\" : {\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }\n" + - "}"; - Function function = this.lookup("springRelease", TestConfiguration.class); - - Message inputMessage = MessageBuilder - .withPayload(payload) - .setHeader(MessageHeaders.CONTENT_TYPE, CloudEventMessageUtils.APPLICATION_CLOUDEVENTS_VALUE + "+json") - .build(); - - assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isFalse(); - - Message resultMessage = (Message) function.apply(inputMessage); - assertThat(resultMessage.getPayload().getReleaseDate()) - .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); - assertThat(resultMessage.getPayload().getVersion()).isEqualTo("2.0"); -// /* -// * Validates that although user only deals with POJO, the framework recognizes -// * both on input and output that it is dealing with Cloud Event and generates -// * appropriate headers/attributes -// */ - assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); - assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(SpringReleaseEvent.class.getName()); - assertThat(CloudEventMessageUtils.getSource(resultMessage)).isEqualTo(URI.create("http://spring.io/")); - } - - @SuppressWarnings("unchecked") - @Test - public void testStructuredPojoToPojoMessageFunction() throws Exception { - String payload = "{\n" + - " \"specversion\" : \"1.0\",\n" + - " \"type\" : \"org.springframework\",\n" + - " \"source\" : \"https://spring.io/\",\n" + - " \"id\" : \"A234-1234-1234\",\n" + - " \"datacontenttype\" : \"application/json\",\n" + - " \"data\" : {\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }\n" + - "}"; - Function function = this.lookup("springReleaseAsMessage", TestConfiguration.class); - - Message inputMessage = MessageBuilder - .withPayload(payload) - .setHeader(MessageHeaders.CONTENT_TYPE, CloudEventMessageUtils.APPLICATION_CLOUDEVENTS_VALUE + "+json") - .build(); - - assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isFalse(); - - Message resultMessage = (Message) function.apply(inputMessage); - assertThat(resultMessage.getPayload().getReleaseDate()) - .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); - assertThat(resultMessage.getPayload().getVersion()).isEqualTo("2.0"); -// /* -// * Validates that although user only deals with POJO, the framework recognizes -// * both on input and output that it is dealing with Cloud Event and generates -// * appropriate headers/attributes -// */ - assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); - assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(SpringReleaseEvent.class.getName()); - assertThat(CloudEventMessageUtils.getSource(resultMessage)).isEqualTo(URI.create("https://spring.release.event")); - } - - @SuppressWarnings("unchecked") - @Test - public void testStructuredPojoToPojoDefaultOutputAttributeProviderNoDataContentType() throws Exception { - String payload = "{\n" + - " \"ce_specversion\" : \"1.0\",\n" + - " \"ce_type\" : \"org.springframework\",\n" + - " \"ce_source\" : \"https://spring.io/\",\n" + - " \"ce_id\" : \"A234-1234-1234\",\n" + - " \"ce_data\" : {\n" + - " \"version\" : \"1.0\",\n" + - " \"releaseName\" : \"Spring Framework\",\n" + - " \"releaseDate\" : \"24-03-2004\"\n" + - " }\n" + - "}"; - Function function = this.lookup("springRelease", TestConfiguration.class); - - Message inputMessage = MessageBuilder - .withPayload(payload) - .setHeader(MessageHeaders.CONTENT_TYPE, CloudEventMessageUtils.APPLICATION_CLOUDEVENTS_VALUE + "+json") - .build(); - assertThat(CloudEventMessageUtils.isCloudEvent(inputMessage)).isFalse(); - - Message resultMessage = (Message) function.apply(inputMessage); - assertThat(resultMessage.getPayload().getReleaseDate()) - .isEqualTo(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); - assertThat(resultMessage.getPayload().getVersion()).isEqualTo("2.0"); - /* - * Validates that although user only deals with POJO, the framework recognizes - * both on input and output that it is dealing with Cloud Event and generates - * appropriate headers/attributes - */ - assertThat(CloudEventMessageUtils.isCloudEvent(resultMessage)).isTrue(); - assertThat(CloudEventMessageUtils.getType(resultMessage)).isEqualTo(SpringReleaseEvent.class.getName()); - assertThat(CloudEventMessageUtils.getSource(resultMessage)).isEqualTo(URI.create("http://spring.io/")); - } - - private Function lookup(String functionDefinition, Class... configClass) { - ApplicationContext context = new SpringApplicationBuilder(configClass).run( - "--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); - return context.getBean(FunctionCatalog.class).lookup(functionDefinition); - } - - @EnableAutoConfiguration - @Configuration - public static class TestConfiguration { - @Bean - Function echo() { - return Function.identity(); - } - - @Bean - Function springRelease() { - return event -> { - try { - event.setReleaseDate(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); - event.setVersion("2.0"); - return event; - } - catch (Exception e) { - throw new IllegalArgumentException(e); - } - }; - } - - @Bean - Function, Flux> springReleaseReactive() { - return flux -> flux.map(event -> { - try { - event.setReleaseDate(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); - event.setVersion("2.0"); - return event; - } - catch (Exception e) { - throw new IllegalArgumentException(e); - } - }); - } - - @Bean - Function, Mono> springReleaseReactiveMono() { - return mono -> mono.map(event -> { - try { - event.setReleaseDate(new SimpleDateFormat("dd-MM-yyyy").parse("01-10-2006")); - event.setVersion("2.0"); - return event; - } - catch (Exception e) { - throw new IllegalArgumentException(e); - } - }); - } - - @Bean - Function, Message> springReleaseAsMessage() { - return message -> { - SpringReleaseEvent updated = springRelease().apply(message.getPayload()); - assertThat(message.getHeaders().get("ce-type")).isEqualTo("org.springframework"); - return CloudEventMessageBuilder.withData(updated) - .copyHeaders(message.getHeaders()) - .setSource("https://spring.release.event") - .setType(SpringReleaseEvent.class.getName()) - .build(); - }; - } - } - - public static class Person { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java deleted file mode 100644 index 0161f6367..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtilsAndBuilderTests.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.cloudevent; - -import java.net.URI; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Oleg Zhurakousky - */ -public class CloudEventMessageUtilsAndBuilderTests { - - @Test// see https://github.com/spring-cloud/spring-cloud-function/issues/805 - public void testHeaderKeyInsensitivity() { - Message httpMessage = MessageBuilder.withPayload("hello") - .setHeader("cE-SoUrCe", "https://foo.bar") - .setHeader("Ce-specVeRsion", "1.0") - .setHeader("Ce-Type", "blah") - .setHeader("x", "x") - .setHeader("zzz", "zzz") - .build(); - - assertThat(CloudEventMessageUtils.isCloudEvent(httpMessage)).isTrue(); - } - - @Test// see https://github.com/spring-cloud/spring-cloud-function/issues/680 - public void testProperAttributeExtractionRegardlessOfTargetProtocol() { - Message ceMessage = CloudEventMessageBuilder.withData("foo").build(); - ceMessage = MessageBuilder.fromMessage(ceMessage).setHeader("target-protocol", "kafka").build(); - - String prefix = CloudEventMessageUtils.determinePrefixToUse(ceMessage.getHeaders()); - assertThat(prefix).isEqualTo("ce-"); - prefix = CloudEventMessageUtils.determinePrefixToUse(ceMessage.getHeaders(), true); - assertThat(prefix).isEqualTo("ce_"); - - String specVersion = CloudEventMessageUtils.getSpecVersion(ceMessage); - assertThat(specVersion).isEqualTo("1.0"); - String type = CloudEventMessageUtils.getType(ceMessage); - assertThat(type).isEqualTo("java.lang.String"); - String id = CloudEventMessageUtils.getId(ceMessage); - assertThat(id).isNotNull(); - URI source = CloudEventMessageUtils.getSource(ceMessage); - assertThat(source.toString()).isEqualTo("https://spring.io/"); - } - - @Test - public void testAttributeRecognitionAndCanonicalization() { - Message httpMessage = MessageBuilder.withPayload("hello") - .setHeader(CloudEventMessageUtils.SOURCE, "https://foo.bar") - .setHeader(CloudEventMessageUtils.SPECVERSION, "1.0") - .setHeader(CloudEventMessageUtils.TYPE, "blah") - .setHeader("x", "x") - .setHeader("zzz", "zzz") - .build(); - Map attributes = CloudEventMessageUtils.getAttributes(httpMessage); - assertThat(attributes.size()).isEqualTo(3); - assertThat((String) CloudEventMessageUtils.getData(httpMessage)).isEqualTo("hello"); - - Message kafkaMessage = CloudEventMessageBuilder.fromMessage(httpMessage).build(CloudEventMessageUtils.KAFKA_ATTR_PREFIX); - attributes = CloudEventMessageUtils.getAttributes(kafkaMessage); - assertThat(attributes.size()).isEqualTo(4); // id will be auto injected, so always at least 4 (as tehre are 4 required attributes in CE) - assertThat(kafkaMessage.getHeaders().get("ce_source")).isNotNull(); - assertThat(CloudEventMessageUtils.getSource(kafkaMessage)).isEqualTo(URI.create("https://foo.bar")); - assertThat(kafkaMessage.getHeaders().get("ce_type")).isNotNull(); - assertThat(CloudEventMessageUtils.getType(kafkaMessage)).isEqualTo("blah"); - assertThat(kafkaMessage.getHeaders().get("ce_specversion")).isNotNull(); - assertThat(CloudEventMessageUtils.getSpecVersion(kafkaMessage)).isEqualTo("1.0"); - - httpMessage = CloudEventMessageBuilder.fromMessage(kafkaMessage).build(CloudEventMessageUtils.DEFAULT_ATTR_PREFIX); - attributes = CloudEventMessageUtils.getAttributes(httpMessage); - assertThat(attributes.size()).isEqualTo(4); // - assertThat(httpMessage.getHeaders().get("ce-source")).isNotNull(); - assertThat(CloudEventMessageUtils.getSource(httpMessage)).isEqualTo(URI.create("https://foo.bar")); - assertThat(httpMessage.getHeaders().get("ce-type")).isNotNull(); - assertThat(CloudEventMessageUtils.getType(httpMessage)).isEqualTo("blah"); - assertThat(httpMessage.getHeaders().get("ce-specversion")).isNotNull(); - assertThat(CloudEventMessageUtils.getSpecVersion(httpMessage)).isEqualTo("1.0"); - } - - @Test - void canonicalizeHeadersWithPossibleCopyReturnsCopyWithUpdatedHeadersWhenModified() { - // TODO add the following test cases - // - // defaultAttrs w/ unmodified keys -> not modified - // defaultAttrs w/ modified keys -> modified - // kafkaAttrs w/ (defaultAttrs+unmodified keys) -> modified - // amqpAttrs -> modified - // structured -> modified - Message inputMessage = MessageBuilder.withPayload("hello") - .setHeader("ce_foo", "bar") - .setHeader("x", "x1") - .setHeader("x|x", "x2") - .build(); - - Message updatedMessage = CloudEventMessageUtils.canonicalizeHeadersWithPossibleCopy(inputMessage); - - assertThat(inputMessage).isNotSameAs(updatedMessage); - assertThat(updatedMessage.getHeaders()) - .containsEntry("ce-foo", "bar") - .containsEntry("x", "x1") - .containsEntry("x|x", "x2"); - } - - @Test - void canonicalizeHeadersWithPossibleCopyReturnsSameInstanceWhenNotModified() { - Message inputMessage = MessageBuilder.withPayload("hello") - .setHeader("ce-foo", "bar") - .setHeader("x", "x1") - .setHeader("x|x", "x2") - .build(); - - Message updatedMessage = CloudEventMessageUtils.canonicalizeHeadersWithPossibleCopy(inputMessage); - - assertThat(inputMessage).isSameAs(updatedMessage); - assertThat(updatedMessage.getHeaders()) - .containsEntry("ce-foo", "bar") - .containsEntry("x", "x1") - .containsEntry("x|x", "x2"); - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/SpringReleaseEvent.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/SpringReleaseEvent.java deleted file mode 100644 index 5afe8953f..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/SpringReleaseEvent.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.cloudevent; - -import java.text.SimpleDateFormat; -import java.util.Date; - -import com.fasterxml.jackson.annotation.JsonFormat; - -/** - * An example POJO that represents cloud event data. - * - * @author Oleg Zhurakousky - * - */ -public class SpringReleaseEvent { - - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") - private Date releaseDate; - - private String releaseName; - - private String version; - - public Date getReleaseDate() { - return releaseDate; - } - - public void setReleaseDate(Date releaseDate) { - this.releaseDate = releaseDate; - } - - public String getReleaseName() { - return releaseName; - } - - public void setReleaseName(String releaseName) { - this.releaseName = releaseName; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - @Override - public String toString() { - return "releaseDate:" + new SimpleDateFormat("dd-MM-yyyy").format(releaseDate) + "; releaseName:" + releaseName + "; version:" + version; - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HeaderMappingTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HeaderMappingTests.java deleted file mode 100644 index 67c3506e9..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HeaderMappingTests.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; - -import static org.assertj.core.api.Assertions.assertThat; - -//NOTE!!! assertions for input in all tests are in 'echo' function since we're validating what's coming into it. -public class HeaderMappingTests { - - @Test - public void testErrorWarnAndContinue() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echoFail.input-header-mapping-expression[0].key1=hello1", - "--spring.cloud.function.configuration.echoFail.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echoFail.input-header-mapping-expression[0].foo=headers.contentType")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("echoFail"); - function.apply(MessageBuilder.withPayload("helo").build()); - } - } - - @Test - public void testInputHeaderMappingPropertyWithIndex() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key1='hello1'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].foo=headers.contentType")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("echo"); - function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); - } - } - - @Test - public void testInputHeaderMappingPropertyWithIndexMix() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key1='hello1'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.foo=headers.contentType")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("echo"); - function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); - } - } - - @Test - public void testInputHeaderMappingPropertyWithIndexMixDeux() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.key1='hello1'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.foo=headers.contentType")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("echo"); - function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); - } - } - - @Test - public void testInputHeaderMappingPropertyWithoutIndex() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.key1='hello1'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.key2='hello2'", - "--spring.cloud.function.configuration.echo.input-header-mapping-expression.foo=headers.contentType")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("echo"); - function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); - } - } - - @Test - public void testInputHeaderMappingExpressionWithCompositionWithIndex() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression[0].key1='hello1'", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression[0].key2='hello2'", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression[0].foo=headers.contentType")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("echo|foo"); - function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); - } - } - - @Test - public void testInputHeaderMappingExpressionWithCompositionWithoutIndex() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression.key1='hello1'", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression.key2='hello2'", - "--spring.cloud.function.configuration.echofoo.input-header-mapping-expression.foo=headers.contentType")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("echo|foo"); - function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); - - //assertions are in 'echo' function since we're validating what's coming into it. - } - } - - @Test - public void testInputHeaderMappingPropertyWithSplitExpression() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key1=headers.path.split('/')[0]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key2=headers.path.split('/')[1]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key3=headers.path")) { - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("split"); - function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") - .setHeader("path", "foo/bar/baz").build()); - } - } - - @SuppressWarnings("unchecked") - @Test - public void testOutputHeaderMapping() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.foo.output-header-mapping-expression.keyOut1='hello1'", - "--spring.cloud.function.configuration.foo.output-header-mapping-expression.keyOut2=headers.contentType")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("foo"); - Message result = (Message) function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); - assertThat(result.getHeaders().containsKey("keyOut1")).isTrue(); - assertThat(result.getHeaders().get("keyOut1")).isEqualTo("hello1"); - assertThat(result.getHeaders().containsKey("keyOut2")).isTrue(); - assertThat(result.getHeaders().get("keyOut2")).isEqualTo("application/json"); - } - } - - @SuppressWarnings("unchecked") - @Test - public void testMixedInputOutputHeaderMapping() throws Exception { - try (ConfigurableApplicationContext context = new SpringApplicationBuilder( - SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut1='hello1'", - "--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut2=headers.contentType", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key1=headers.path.split('/')[0]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key2=headers.path.split('/')[1]", - "--spring.cloud.function.configuration.split.input-header-mapping-expression.key3=headers.path")) { - - FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup("split"); - Message result = (Message) function.apply(MessageBuilder.withPayload("helo") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") - .setHeader("path", "foo/bar/baz").build()); - assertThat(result.getHeaders().containsKey("keyOut1")).isTrue(); - assertThat(result.getHeaders().get("keyOut1")).isEqualTo("hello1"); - assertThat(result.getHeaders().containsKey("keyOut2")).isTrue(); - assertThat(result.getHeaders().get("keyOut2")).isEqualTo("application/json"); - } - } - - @EnableAutoConfiguration - @Configuration - protected static class SampleFunctionConfiguration { - - @Bean - public Function, Message> echo() { - return m -> { - assertThat(m.getHeaders().get("key1")).isEqualTo("hello1"); - assertThat(m.getHeaders().get("key2")).isEqualTo("hello2"); - assertThat(m.getHeaders().get("foo")).isEqualTo("application/json"); - return m; - }; - } - - @Bean - public Function, Message> echoFail() { - return m -> { - assertThat(m.getHeaders().containsKey("key1")).isFalse(); - assertThat(m.getHeaders().get("key2")).isEqualTo("hello2"); - assertThat(m.getHeaders().containsKey("foo")).isFalse(); - return m; - }; - } - - @Bean - public Function, Message> split() { - return m -> { - assertThat(m.getHeaders().get("key1")).isEqualTo("foo"); - assertThat(m.getHeaders().get("key2")).isEqualTo("bar"); - assertThat(m.getHeaders().get("key3")).isEqualTo("foo/bar/baz"); - return m; - }; - } - - @Bean - public Function, Message> foo() { - return x -> { - assertThat(x.getHeaders().containsKey("keyOut1")).isFalse(); - return x; - }; - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HybridFunctionalRegistrationTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HybridFunctionalRegistrationTests.java deleted file mode 100644 index fd8b3d843..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HybridFunctionalRegistrationTests.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; -import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class HybridFunctionalRegistrationTests { - - // see https://github.com/spring-cloud/spring-cloud-function/issues/258 - @SuppressWarnings("rawtypes") - @Test - public void testNoDoubleRegistrationInHybridMode() { - ConfigurableApplicationContext context = FunctionalSpringApplication - .run(UppercaseFunction.class, "--spring.functional.enabled=false"); - - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - - assertThat(context.containsBean("function")).isTrue(); - assertThat(context.getBeansOfType(UppercaseFunction.class).size()).isEqualTo(1); - assertThat((Function) catalog.lookup("hybridFunctionalRegistrationTests.UppercaseFunction")).isNotNull(); - } - - @SuppressWarnings("rawtypes") - @Test - public void testMessageHeaderPropagationInFunctionalBeanRegistration() { - ConfigurableApplicationContext context = FunctionalSpringApplication - .run(UppercaseMessageFunction.class, "--spring.functional.enabled=false"); - - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - - assertThat(context.containsBean("function")).isTrue(); - assertThat(context.getBeansOfType(UppercaseMessageFunction.class).size()).isEqualTo(1); - Function f = catalog.lookup(Function.class, "hybridFunctionalRegistrationTests.UppercaseMessageFunction"); - assertThat(f).isNotNull(); - String result = (String) f.apply(MessageBuilder.withPayload("hello").setHeader("foo", "foo").setHeader("blah", "blah").build()); - assertThat(result).isEqualTo("HELLO"); - } - - @SuppressWarnings("rawtypes") - @Test - public void testNoDoubleRegistrationInHybridModeFluxedFunction() { - ConfigurableApplicationContext context = FunctionalSpringApplication - .run(UppercaseFluxFunction.class, "--spring.functional.enabled=false"); - - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - - assertThat(context.containsBean("function")).isTrue(); - assertThat(context.getBeansOfType(UppercaseFluxFunction.class).size()).isEqualTo(1); - assertThat((Function) catalog.lookup(Function.class, "hybridFunctionalRegistrationTests.UppercaseFluxFunction")).isNotNull(); - } - - @SpringBootConfiguration - @ImportAutoConfiguration({ - ContextFunctionCatalogAutoConfiguration.class, - JacksonAutoConfiguration.class } - ) - public static class UppercaseFunction implements Function { - - @Override - public String apply(String t) { - return t.toUpperCase(); - } - } - - @SpringBootConfiguration - @ImportAutoConfiguration({ - ContextFunctionCatalogAutoConfiguration.class, - JacksonAutoConfiguration.class } - ) - public static class UppercaseMessageFunction implements Function, String> { - - @Override - public String apply(Message message) { - assertThat(message.getHeaders().get("foo")).isEqualTo("foo"); - assertThat(message.getHeaders().get("blah")).isEqualTo("blah"); - return message.getPayload().toUpperCase(); - } - } - - @SpringBootConfiguration - @ImportAutoConfiguration({ - ContextFunctionCatalogAutoConfiguration.class, - JacksonAutoConfiguration.class } - ) - public static class UppercaseFluxFunction implements Function, Flux> { - - @Override - public Flux apply(Flux flux) { - return flux.map(v -> v.toUpperCase()); - } - - - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/MessageRoutingCallbackTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/MessageRoutingCallbackTests.java deleted file mode 100644 index 11dfc1daf..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/MessageRoutingCallbackTests.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.function.Function; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -import static org.assertj.core.api.Assertions.assertThat; - -public class MessageRoutingCallbackTests { - - private ApplicationContext context; - - @BeforeEach - public void before() { - System.clearProperty("spring.cloud.function.definition"); - } - - @SuppressWarnings("unchecked") - @Test - public void testRoutingCallbackWithMessageModification() { - FunctionCatalog catalog = this.configureCatalog(SamppleConfiguration.class); - SamppleConfiguration conf = context.getBean(SamppleConfiguration.class); - FunctionInvocationWrapper function = (FunctionInvocationWrapper) catalog.lookup(RoutingFunction.FUNCTION_NAME, "application/json"); - String foo = "{\"foo\":\"blah\"}"; - Message fooResult = (Message) function.apply(MessageBuilder.withPayload(foo.getBytes()).build()); - String bar = "{\"bar\":\"blah\"}"; - Message barResult = (Message) function.apply(MessageBuilder.withPayload(bar.getBytes()).build()); - assertThat(fooResult.getPayload()).isEqualTo("\"foo\"".getBytes()); - assertThat(barResult.getPayload()).isEqualTo("\"bar\"".getBytes()); - - assertThat(fooResult.getHeaders().get("originalId")).isEqualTo(conf.createdMessageIds.get("foo")); - assertThat(barResult.getHeaders().get("originalId")).isEqualTo(conf.createdMessageIds.get("bar")); - } - - private FunctionCatalog configureCatalog(Class... configClass) { - this.context = new SpringApplicationBuilder(configClass) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - return catalog; - } - - @EnableAutoConfiguration - private static class SamppleConfiguration { - - Map createdMessageIds = new HashMap<>(); - - @Bean - public MessageRoutingCallback messageRoutingCallback(JsonMapper jsonMapper) { - return new MessageRoutingCallback() { - - @Override - public String routingResult(Message message) { - String payload = new String((byte[]) message.getPayload()); - - MessageBuilder builder; - String functionDefinition; - if (payload.contains("foo")) { - builder = MessageBuilder.withPayload(jsonMapper.fromJson(payload, Foo.class)); - functionDefinition = "foo"; - } - else { - builder = MessageBuilder.withPayload(jsonMapper.fromJson(payload, Bar.class)); - functionDefinition = "bar"; - } - Message m = builder.copyHeaders(message.getHeaders()).build(); - createdMessageIds.put(functionDefinition, m.getHeaders().getId()); - return functionDefinition; - } - }; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Bean - public Function, Message> foo() { - return foo -> { - Message m = MessageBuilder.withPayload("foo").setHeader("originalId", foo.getHeaders().getId()).build(); - createdMessageIds.put("foo", foo.getHeaders().getId()); - return m; - }; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Bean - public Function, Message> bar() { - return bar -> { - Message m = MessageBuilder.withPayload("bar").setHeader("originalId", bar.getHeaders().getId()).build(); - createdMessageIds.put("bar", bar.getHeaders().getId()); - return m; - }; - } - } - - - public static class Foo { - private String foo; - - public String getFoo() { - return foo; - } - - public void setFoo(String foo) { - this.foo = foo; - } - } - - public static class Bar { - private String bar; - - public String getBar() { - return bar; - } - - public void setBar(String bar) { - this.bar = bar; - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryMultiInOutTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryMultiInOutTests.java deleted file mode 100644 index 4039e1cd2..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryMultiInOutTests.java +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.function.BiFunction; -import java.util.function.Function; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuple3; -import reactor.util.function.Tuples; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.converter.AbstractMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeTypeUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class BeanFactoryAwareFunctionRegistryMultiInOutTests { - - private FunctionCatalog configureCatalog() { - ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG"); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - return catalog; - } - - /* - * This test validates , Flux> without any type conversion - */ - @Test - public void testMultiInput() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple"); - Flux stringStream = Flux.just("one", "two", "three"); - Flux intStream = Flux.just(1, 2, 3); - - List result = multiInputFunction.apply(Tuples.of(stringStream, intStream)).collectList().block(); - assertThat(result.get(0).equals("one-1")); - assertThat(result.get(1).equals("one-2")); - assertThat(result.get(2).equals("one-3")); - } - - @Test - public void testMultiInputWithPojoConversion() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("thomas", "application/json"); - CartEvent carEvent = new CartEvent(); - carEvent.setCarEvent("carEvent"); - Flux carEventStream = Flux.just(carEvent); - - CheckoutEvent checkoutEvent = new CheckoutEvent(); - checkoutEvent.setCheckoutEvent("checkoutEvent"); - Flux checkoutEventStream = Flux.just(checkoutEvent); - - Tuple2, Flux> streams = Tuples.of(carEventStream, checkoutEventStream); - - List result = multiInputFunction.apply(streams).collectList().block(); - System.out.println(result); - } - - @SuppressWarnings("unused") - @Test - @Disabled - public void testMultiInputBiFunction() { - FunctionCatalog catalog = this.configureCatalog(); - BiFunction, Flux, Flux> multiInputFunction = - catalog.lookup(BiFunction.class, "multiInputSingleOutputViaBiFunction"); - Flux stringStream = Flux.just("one", "two", "three"); - Flux intStream = Flux.just(1, 2, 3); - -// List result = multiInputFunction.apply(Tuples.of(stringStream, intStream)).collectList().block(); -// System.out.println(result); - } - - /* - * This test invokes the same function as above but with types reversed. - * While the target function remains , Flux> - * it is actually invoked as Tuple2, Flux> - * hence showcasing type conversion using Spring's ConversionService - */ - @Test - public void testMultiInputWithConversion() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple"); - Flux stringStream = Flux.just(11, 22, 33); - Flux intStream = Flux.just("1", "2", "2"); - - List result = multiInputFunction.apply(Tuples.of(stringStream, intStream)).collectList().block(); - assertThat(result.get(0).equals("11-1")); - assertThat(result.get(1).equals("22-2")); - assertThat(result.get(2).equals("33-3")); - } - - /* - * Same as above but with composing 'uppercase' function essentially validating \ - * composition in multi-input scenario - */ - @Test - @Disabled - public void testMultiInputWithComposition() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple|uppercase"); - Flux stringStream = Flux.just("one", "two", "three"); - Flux intStream = Flux.just("1", "2", "3"); - - List result = multiInputFunction.apply(Tuples.of(stringStream, intStream)).collectList().block(); - System.out.println(result); - } - - /* - * This is basically the repeater function currently prototyped in Riff - * The only difference it uses Tuple2 instead of BiFunction (which we will support anyway) - */ - @Test - public void testMultiOutputAsArray() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux[]> repeater = - catalog.lookup("repeater"); - Flux stringStream = Flux.just("one", "two", "three"); - Flux intStream = Flux.just(3, 2, 1); - - Flux[] result = repeater.apply(Tuples.of(stringStream, intStream)); - result[0].subscribe(System.out::println); - result[1].subscribe(System.out::println); - } - - - /* - * This test demonstrates single input into multiple outputs - * as Tuple3 thus making output types known. - * - * The input is a POJO (Person) - * no conversion - */ - @Test - public void testMultiOutputAsTuplePojoInInputTypeMatch() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Tuple3, Flux, Flux>> multiOutputFunction = - catalog.lookup("multiOutputAsTuplePojoIn"); - Flux personStream = Flux.just(new Person("Uncle Sam", 1), new Person("Oncle Pierre", 2)); - - Tuple3, Flux, Flux> result = multiOutputFunction.apply(personStream); - result.getT1().subscribe(v -> System.out.println("=> 1: " + v)); - result.getT2().subscribe(v -> System.out.println("=> 2: " + v)); - result.getT3().subscribe(v -> System.out.println("=> 3: " + v)); - } - - /* - * This test is identical to the previous one with the exception that the - * input is a Message with payload as JSON byte array representation of Person (expected by the target function), - * thus demonstrating Message Conversion - */ - @Test - public void testMultiOutputAsTuplePojoInInputByteArray() { - FunctionCatalog catalog = this.configureCatalog(); - Function>, Tuple3, Flux, Flux>> multiOutputFunction = - catalog.lookup("multiOutputAsTuplePojoIn"); - - Message uncleSam = MessageBuilder.withPayload("{\"name\":\"Uncle Sam\",\"id\":1}".getBytes(StandardCharsets.UTF_8)) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(); - Message unclePierre = MessageBuilder.withPayload("{\"name\":\"Oncle Pierre\",\"id\":2}".getBytes(StandardCharsets.UTF_8)) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(); - Flux> personStream = Flux.just(uncleSam, unclePierre); - - Tuple3, Flux, Flux> result = multiOutputFunction.apply(personStream); - result.getT1().subscribe(v -> System.out.println("=> 1: " + v)); - result.getT2().subscribe(v -> System.out.println("=> 2: " + v)); - result.getT3().subscribe(v -> System.out.println("=> 3: " + v)); - } - - /* - * This is another variation of the above. In this case the signature of the target function is - * >, Tuple3, Flux, Flux>> yet we are sending - * Message with payload as byte[] which is converted to Person and then embedded in new Message - * passed to a function - */ - @Test - public void testMultiOutputAsTuplePojoInInputByteArrayInputTypePojoMessage() { - FunctionCatalog catalog = this.configureCatalog(); - Function>, Tuple3, Flux, Flux>> multiOutputFunction = - catalog.lookup("multiOutputAsTupleMessageIn"); - - Message uncleSam = MessageBuilder.withPayload("{\"name\":\"Uncle Sam\",\"id\":1}".getBytes(StandardCharsets.UTF_8)) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(); - Message unclePierre = MessageBuilder.withPayload("{\"name\":\"Oncle Pierre\",\"id\":2}".getBytes(StandardCharsets.UTF_8)) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(); - Flux> personStream = Flux.just(uncleSam, unclePierre); - - Tuple3, Flux, Flux> result = multiOutputFunction.apply(personStream); - result.getT1().subscribe(v -> System.out.println("=> 1: " + v)); - result.getT2().subscribe(v -> System.out.println("=> 2: " + v)); - result.getT3().subscribe(v -> System.out.println("=> 3: " + v)); - } - - @Test - public void testMultiToMulti() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux, Flux>, Tuple2, Mono>> multiToMulti = - catalog.lookup("multiToMulti"); - - Flux firstFlux = Flux.just("Unlce", "Oncle"); - Flux secondFlux = Flux.just("Sam", "Pierre"); - Flux thirdFlux = Flux.just(1, 2); - - Tuple2, Mono> result = multiToMulti.apply(Tuples.of(firstFlux, secondFlux, thirdFlux)); - result.getT1().subscribe(v -> System.out.println("=> 1: " + v)); - result.getT2().subscribe(v -> System.out.println("=> 2: " + v)); - } - - @Test - @Disabled - public void testMultiToMultiWithMessageByteArrayPayload() { - FunctionCatalog catalog = this.configureCatalog(); - Function>, Flux>, Flux>>, Tuple2>, Mono>>> multiTuMulti = - catalog.lookup("multiToMulti", "foo/bar,application/json", "application/json"); - - Flux> firstFlux = Flux.just( - MessageBuilder.withPayload("Unlce".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build(), - MessageBuilder.withPayload("Onlce".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build()); - Flux> secondFlux = Flux.just( - MessageBuilder.withPayload("Sam".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build(), - MessageBuilder.withPayload("Pierre".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build()); - - ByteBuffer one = ByteBuffer.allocate(4); - one.putInt(1); - ByteBuffer two = ByteBuffer.allocate(4); - two.putInt(2); - - Flux> thirdFlux = Flux.just( - MessageBuilder.withPayload(one.array()).setHeader(MessageHeaders.CONTENT_TYPE, "octet-stream/integer").build(), - MessageBuilder.withPayload(two.array()).setHeader(MessageHeaders.CONTENT_TYPE, "octet-stream/integer").build()); - - Tuple2>, Mono>> result = multiTuMulti.apply(Tuples.of(firstFlux, secondFlux, thirdFlux)); - ObjectMapper mapper = new ObjectMapper(); - result.getT1().subscribe(v -> { - try { - System.out.println("=> 1: " + mapper.readValue(v.getPayload(), Person.class)); - } - catch (Exception e) { - e.printStackTrace(); - } - }); - result.getT2().subscribe(v -> { - try { - System.out.println("=> 2: " + mapper.readValue(v.getPayload(), Long.class)); - } - catch (Exception e) { - e.printStackTrace(); - } - }); - } - - - @EnableAutoConfiguration - @Configuration - protected static class SampleFunctionConfiguration { - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - // ============= MULTI-INPUT and MULTI-OUTPUT functions ============ - - @Bean - public Function, Flux>, Flux> multiInputSingleOutputViaReactiveTuple() { - return tuple -> { - Flux stringStream = tuple.getT1(); - Flux intStream = tuple.getT2(); - return Flux.zip(stringStream, intStream, (string, integer) -> string + "-" + integer); - }; - } - - @Bean - public BiFunction, Flux, Flux> multiInputSingleOutputViaBiFunction() { - return (in1, in2) -> { - Flux stringStream = in1; - Flux intStream = in2; - return Flux.zip(stringStream, intStream, (string, integer) -> string + "-" + integer); - }; - } - - @Bean - public Function, Tuple3, Flux, Flux>> multiOutputAsTuplePojoIn() { - return flux -> { - Flux pubSubFlux = flux.publish().autoConnect(3); - Flux nameFlux = pubSubFlux.map(person -> person.getName()); - Flux idFlux = pubSubFlux.map(person -> person.getId()); - return Tuples.of(pubSubFlux, nameFlux, idFlux); - }; - } - - @Bean - public Function>, Tuple3, Flux, Flux>> multiOutputAsTupleMessageIn() { - return flux -> { - Flux pubSubFlux = flux.map(message -> message.getPayload()).publish().autoConnect(3); - Flux nameFlux = pubSubFlux.map(person -> person.getName()); - Flux idFlux = pubSubFlux.map(person -> person.getId()); - return Tuples.of(pubSubFlux, nameFlux, idFlux); - }; - } - - @Bean - public Function, Flux, Flux>, Tuple2, Mono>> multiToMulti() { - return tuple -> { - Flux toStringFlux = tuple.getT1(); - Flux nameFlux = tuple.getT2(); - Flux idFlux = tuple.getT3(); - Flux person = toStringFlux.zipWith(nameFlux) - .map(t -> t.getT1() + " " + t.getT2()) - .zipWith(idFlux) - .map(t -> new Person(t.getT1(), t.getT2())); - return Tuples.of(person, person.count()); - }; - } - - @Bean - public MessageConverter byteArrayToIntegerMessageConverter() { - return new AbstractMessageConverter(MimeTypeUtils.parseMimeType("octet-stream/integer")) { - - @Override - protected boolean supports(Class clazz) { - return Integer.class.isAssignableFrom(clazz); - } - - @Override - protected Object convertFromInternal( - Message message, Class targetClass, @Nullable Object conversionHint) { - ByteBuffer wrappedPayload = ByteBuffer.wrap((byte[]) message.getPayload()); - return wrappedPayload.getInt(); - } - - @Override - protected Object convertToInternal( - Object payload, @Nullable MessageHeaders headers, @Nullable Object conversionHint) { - - return null; - } - }; - } - - @Bean - public Function, Flux>, Flux[]> repeater() { - - return tuple -> { - Flux stringFlux = tuple.getT1(); - Flux integerFlux = tuple.getT2(); - - Flux sharedIntFlux = integerFlux.publish().autoConnect(2); - - Flux repeated = stringFlux - .zipWith(sharedIntFlux) - .flatMap(t -> Flux.fromIterable(Collections.nCopies(t.getT2(), t.getT1()))); - - Flux sum = sharedIntFlux - .buffer(3, 1) - .map(l -> l.stream().mapToInt(Integer::intValue).sum()); - - return new Flux[] { repeated, sum }; - }; - } - - @Bean - public Function, Flux>, Flux> thomas() { - return tuple -> { - Flux cartEventStream = tuple.getT1(); - Flux checkoutEventStream = tuple.getT2(); - - return Flux.zip(cartEventStream, checkoutEventStream, (cartEvent, checkoutEvent) -> { - OrderEvent oe = new OrderEvent(); - oe.setOrderEvent(cartEvent.toString() + "- " + checkoutEvent.toString()); - return oe; - }); - }; - } - } - - public static class CartEvent { - private String carEvent; - - public String getCarEvent() { - return carEvent; - } - - public void setCarEvent(String carEvent) { - this.carEvent = carEvent; - } - } - - public static class CheckoutEvent { - private String checkoutEvent; - - public String getCheckoutEvent() { - return checkoutEvent; - } - - public void setCheckoutEvent(String checkoutEvent) { - this.checkoutEvent = checkoutEvent; - } - } - - public static class OrderEvent { - private String orderEvent; - - public String getOrderEvent() { - return orderEvent; - } - - public void setOrderEvent(String orderEvent) { - this.orderEvent = orderEvent; - } - } - - public static class Person { - private String name; - private int id; - public Person() { - - } - public Person(String name, int id) { - this.name = name; - this.id = id; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public int getId() { - return id; - } - public void setId(int id) { - this.id = id; - } - @Override - public String toString() { - return "Person: " + name + "/" + id; - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java deleted file mode 100644 index ebeb9dd94..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java +++ /dev/null @@ -1,1436 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - - -import java.io.Serializable; -import java.lang.reflect.Field; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.databind.JsonNode; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuple3; -import reactor.util.function.Tuples; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.converter.AbstractMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.support.GenericMessage; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.stereotype.Component; -import org.springframework.util.MimeType; -import org.springframework.util.MimeTypeUtils; -import org.springframework.util.ReflectionUtils; - -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class BeanFactoryAwareFunctionRegistryTests { - - private ApplicationContext context; - - private FunctionCatalog configureCatalog() { - return this.configureCatalog(SampleFunctionConfiguration.class); - } - - private FunctionCatalog configureCatalog(Class... configClass) { - this.context = new SpringApplicationBuilder(configClass) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - return catalog; - } - - @BeforeEach - public void before() { - System.clearProperty("spring.cloud.function.definition"); - } - - @Test - public void testFunctionEligibilityFiltering() { - System.setProperty("spring.cloud.function.ineligible-definitions", "asJsonNode"); - Collection registeredFunction = new ArrayList(); - FunctionCatalog catalog = this.configureCatalog(JsonNodeConfiguration.class); - for (String beanName : context.getBeanDefinitionNames()) { - try { - FunctionInvocationWrapper function = catalog.lookup(beanName); - if (function != null && function.getFunctionDefinition().equals(beanName)) { - registeredFunction.add(function); - } - } - catch (Exception e) { - // ignore - } - } - System.out.println(registeredFunction); - assertThat(registeredFunction.size()).isEqualTo(2); - assertThat((FunctionInvocationWrapper) catalog.lookup("asJsonNode")).isNull(); - } - - @Test - public void testJsonNodeAsInput() throws Exception { - FunctionCatalog catalog = this.configureCatalog(JsonNodeConfiguration.class); - Function, Message> f = catalog.lookup("messageAsJsonNode", "application/json"); - Message m = MessageBuilder.withPayload("[{\"name\":\"bob\"}, {\"name\":\"bob\"}]").setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build(); - assertThat(new String(f.apply(m).getPayload())).isEqualTo("[{\"name\":\"bob\"},{\"name\":\"bob\"}]"); - f = catalog.lookup("asJsonNode", "application/json"); - assertThat(new String(f.apply(m).getPayload())).isEqualTo("[{\"name\":\"bob\"},{\"name\":\"bob\"}]"); - } - - @SuppressWarnings({ "rawtypes" }) - @Test - public void concurrencyLookupTest() throws Exception { - FunctionCatalog catalog = this.configureCatalog(); - ExecutorService executor = Executors.newCachedThreadPool(); - for (int i = 0; i < 100; i++) { - executor.execute(() -> { - catalog.lookup("uppercase", "application/json"); - }); - executor.execute(() -> { - catalog.lookup("numberword", "application/json"); - }); - } - Thread.sleep(1000); - Field frField = ReflectionUtils.findField(catalog.getClass(), "functionRegistrations"); - frField.setAccessible(true); - Collection c = (Collection) frField.get(catalog); - assertThat(c.size()).isEqualTo(2); - } - - @Test - public void testReturnedMessageIsUnmodified() throws Exception { - FunctionCatalog catalog = this.configureCatalog(); - Function, Message> function = catalog.lookup("uppercaseMessage", "application/json"); - assertThat(function).isNotNull(); - - Message result = function.apply(MessageBuilder.withPayload("bob").setHeader("foo", "foo").build()); - assertThat(result.getHeaders().containsKey("foo")).isFalse(); - assertThat(result.getHeaders().containsKey("bar")).isTrue(); - } - - @SuppressWarnings("unchecked") - @Test - public void testDefaultLookup() throws Exception { - FunctionCatalog catalog = this.configureCatalog(); - Object function = catalog.lookup(""); - assertThat(function).isNull(); - //== - System.setProperty("spring.cloud.function.definition", "uppercase"); - catalog = this.configureCatalog(); - function = catalog.lookup(""); - assertThat(function).isNotNull(); -// Field field = ReflectionUtils.findField(FunctionInvocationWrapper.class, "composed"); -// field.setAccessible(true); - assertThat(((FunctionInvocationWrapper) function).isComposed()).isFalse(); - //== - System.setProperty("spring.cloud.function.definition", "uppercase|uppercaseFlux"); - catalog = this.configureCatalog(); -// function = catalog.lookup("", "application/json"); - function = catalog.lookup(""); - Function, Flux>> typedFunction = (Function, Flux>>) function; - Object blockFirst = typedFunction.apply(Flux.just("hello")).blockFirst(); - System.out.println(blockFirst); - assertThat(function).isNotNull(); -// field = ReflectionUtils.findField(FunctionInvocationWrapper.class, "composed"); -// field.setAccessible(true); -// assertThat(((boolean) field.get(function))).isTrue(); - assertThat(((FunctionInvocationWrapper) function).isComposed()).isTrue(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testBiFunction() { - FunctionCatalog catalog = this.configureCatalog(); - - Function biFunction = catalog.lookup("biFuncUpperCase"); - assertThat(biFunction.apply("hello")).isEqualTo("HELLO"); - } - - @Test - public void testImperativeFunction() { - FunctionCatalog catalog = this.configureCatalog(); - - Function>, Flux>> messageFlux = catalog.lookup("uppercase", "application/json"); - Message message1 = MessageBuilder.withPayload("\"uppercaseFlux\"".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build(); - Message message2 = MessageBuilder.withPayload("\"uppercaseFlux2\"".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build(); - List> messageResult = messageFlux.apply(Flux.just(message1, message2)).collectList().block(); - assertThat(messageResult.get(0).getPayload()).isEqualTo("\"UPPERCASEFLUX\"".getBytes(StandardCharsets.UTF_8)); - assertThat(messageResult.get(1).getPayload()).isEqualTo("\"UPPERCASEFLUX2\"".getBytes(StandardCharsets.UTF_8)); - } - - @Test - public void testConsumerFunction() { // function that returns Void, effectively a Consumer - FunctionCatalog catalog = this.configureCatalog(); - - Function consumerFunction = catalog.lookup("consumerFunction"); - assertThat(consumerFunction.apply("hello")).isNull(); - - Function, Void> consumerFunctionAsMessageA = catalog.lookup("consumerFunction"); - assertThat(consumerFunctionAsMessageA.apply(new GenericMessage("\"hello\"".getBytes()))).isNull(); - - Function, Void> consumerFunctionAsMessageB = catalog.lookup("consumerFunction", "application/json"); - assertThat(consumerFunctionAsMessageB.apply(new GenericMessage("\"hello\"".getBytes()))).isNull(); - } - - @Test - public void testMessageToPojoConversion() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Person> uppercasePerson = catalog.lookup("uppercasePerson"); - Person person = uppercasePerson.apply(MessageBuilder.withPayload("{\"name\":\"bill\",\"id\":2}").build()); - assertThat(person.getName()).isEqualTo("BILL"); - } - - /* - * When invoking imperative function as reactive the rules are - * - the input wrapper must match the output wrapper (e.g., or ) - */ - @Test - public void testImperativeVoidInputFunction() { - FunctionCatalog catalog = this.configureCatalog(); - - Function anyInputSignature = catalog.lookup("voidInputFunction"); - assertThat(anyInputSignature.apply(null)).isEqualTo("voidInputFunction"); - - Function asVoid = catalog.lookup("voidInputFunction"); - assertThat(asVoid.apply(null)).isEqualTo("voidInputFunction"); - } - - @Test - public void testReactiveVoidInputFunction() { - FunctionCatalog catalog = this.configureCatalog(); - - Function, Flux> voidInputFunctionReactive = catalog.lookup("voidInputFunctionReactive"); - List resultList = voidInputFunctionReactive.apply(Flux.empty()).collectList().block(); - assertThat(resultList.get(0)).isEqualTo("voidInputFunctionReactive"); - - Function> asVoid = catalog.lookup("voidInputFunctionReactive"); - resultList = asVoid.apply(null).collectList().block(); - assertThat(resultList.get(0)).isEqualTo("voidInputFunctionReactive"); - } - - @Test - public void testReactiveVoidInputFunctionAsSupplier() { - FunctionCatalog catalog = this.configureCatalog(); - Supplier> functionAsSupplier = catalog.lookup("voidInputFunctionReactive"); - List resultList = functionAsSupplier.get().collectList().block(); - assertThat(resultList.get(0)).isEqualTo("voidInputFunctionReactive"); - - Supplier> functionAsSupplier2 = catalog.lookup("voidInputFunctionReactive2"); - resultList = functionAsSupplier2.get().collectList().block(); - assertThat(resultList.get(0)).isEqualTo("voidInputFunctionReactive2"); - } - - - @Test - public void testComposition() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux> fluxFunction = catalog.lookup("uppercase|reverseFlux"); - - List result = fluxFunction.apply(Flux.just("hello", "bye")).collectList().block(); - assertThat(result.get(0)).isEqualTo("OLLEH"); - assertThat(result.get(1)).isEqualTo("EYB"); - - fluxFunction = catalog.lookup("uppercase|reverse|reverseFlux"); - result = fluxFunction.apply(Flux.just("hello", "bye")).collectList().block(); - assertThat(result.get(0)).isEqualTo("HELLO"); - assertThat(result.get(1)).isEqualTo("BYE"); - - fluxFunction = catalog.lookup("uppercase|reverseFlux|reverse"); - result = fluxFunction.apply(Flux.just("hello", "bye")).collectList().block(); - assertThat(result.get(0)).isEqualTo("HELLO"); - assertThat(result.get(1)).isEqualTo("BYE"); - - fluxFunction = catalog.lookup("uppercase|reverse"); - result = fluxFunction.apply(Flux.just("hello", "bye")).collectList().block(); - assertThat(result.get(0)).isEqualTo("OLLEH"); - assertThat(result.get(1)).isEqualTo("EYB"); - - Function function = catalog.lookup("uppercase|reverse"); - assertThat(function.apply("foo")).isEqualTo("OOF"); - } - - @Test - public void testCompositionSupplierAndFunction() { - FunctionCatalog catalog = this.configureCatalog(); - - Supplier> numberSupplierFlux = catalog.lookup("numberword|uppercaseFlux"); - String result = numberSupplierFlux.get().blockFirst(); - assertThat(result).isEqualTo("ONE"); - } - - /* - * This test should fail since the actual function is , hence we can - * not possibly convert Flux (which implies "many") to a single string. - * Further more, such flux will need to be triggered (e.g., subscribe(..) ) - */ - @SuppressWarnings("unused") - @Test - public void testReactiveFunctionWithImperativeInputAndOutputFail() { - FunctionCatalog catalog = this.configureCatalog(); - Function reverse = catalog.lookup("reverseFlux"); - Assertions.assertThrows(ClassCastException.class, () -> { - String result = reverse.apply("reverseFlux"); - }); - } - - @Test - public void testCompositionWithOutputConversion() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>> fluxFunction = catalog.lookup("uppercase|reverseFlux", "application/json"); - List> result = fluxFunction.apply(Flux.just("hello", "bye")).collectList().block(); - assertThat(result.get(0).getPayload()).isEqualTo("\"OLLEH\"".getBytes()); - assertThat(result.get(1).getPayload()).isEqualTo("\"EYB\"".getBytes()); - - fluxFunction = catalog.lookup("uppercase|reverse|reverseFlux", "application/json"); - result = fluxFunction.apply(Flux.just("hello", "bye")).collectList().block(); - assertThat(result.get(0).getPayload()).isEqualTo("\"HELLO\"".getBytes()); - assertThat(result.get(1).getPayload()).isEqualTo("\"BYE\"".getBytes()); - - fluxFunction = catalog.lookup("uppercase|reverseFlux|reverse", "application/json"); - result = fluxFunction.apply(Flux.just("hello", "bye")).collectList().block(); - assertThat(result.get(0).getPayload()).isEqualTo("\"HELLO\"".getBytes()); - assertThat(result.get(1).getPayload()).isEqualTo("\"BYE\"".getBytes()); - } - - @Test - public void testReactiveFunctionWithImperativeInputReactiveOutput() { - FunctionCatalog catalog = this.configureCatalog(); - Function> reverse = catalog.lookup("reverseFlux"); - List result = reverse.apply("reverse").collectList().block(); - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0)).isEqualTo("esrever"); - } - - @Test - public void testMonoVoidToMonoVoid() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Mono> monoToMono = catalog.lookup("monoVoidToMonoVoid"); - Void block = monoToMono.apply(Mono.empty()).block(); - assertThat(block).isNull(); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void textTypeConversionWithComplexInputType() { - FunctionCatalog catalog = this.configureCatalog(ComplexTypeFunctionConfiguration.class); - Function function = catalog.lookup("function"); - - // as String - String result = (String) function.apply("{\"key\":\"purchase\",\"data\":{\"name\":\"bike\"}}"); - assertThat(result).isEqualTo("BIKE"); - - // as byte[] - result = (String) function.apply("{\"key\":\"purchase\",\"data\":{\"name\":\"bike\"}}".getBytes()); - assertThat(result).isEqualTo("BIKE"); - - // as Message - result = (String) function.apply(MessageBuilder.withPayload("{\"key\":\"purchase\",\"data\":{\"name\":\"bike\"}}").build()); - assertThat(result).isEqualTo("BIKE"); - - // as Message - result = (String) function.apply(MessageBuilder.withPayload("{\"key\":\"purchase\",\"data\":{\"name\":\"bike\"}}".getBytes()).build()); - assertThat(result).isEqualTo("BIKE"); - } - - // MULTI INPUT/OUTPUT - - - @Test - public void testMultiInput() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple"); - Flux stringStream = Flux.just("one", "two", "three"); - Flux intStream = Flux.just(1, 2, 3); - - List result = multiInputFunction.apply(Tuples.of(stringStream, intStream)).collectList().block(); - assertThat(result.size()).isEqualTo(3); - assertThat(result.get(0)).isEqualTo("one-1"); - assertThat(result.get(1)).isEqualTo("two-2"); - assertThat(result.get(2)).isEqualTo("three-3"); - } - - - //@Test - public void testMultiInputWithComposition() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Flux>, Flux> multiInputFunction = - catalog.lookup("multiInputSingleOutputViaReactiveTuple|uppercase"); - Flux stringStream = Flux.just("one", "two", "three"); - Flux intStream = Flux.just("1", "2", "3"); - - List result = multiInputFunction.apply(Tuples.of(stringStream, intStream)).collectList().block(); - assertThat(result.size()).isEqualTo(3); - assertThat(result.get(0)).isEqualTo("ONE-1"); - assertThat(result.get(1)).isEqualTo("TWO-2"); - assertThat(result.get(2)).isEqualTo("THREE-3"); - } - - - @Test - public void testMultiOutput() { - FunctionCatalog catalog = this.configureCatalog(); - Function, Tuple3, Flux, Flux>> multiOutputFunction = - catalog.lookup("multiOutputAsTuple"); - Flux personStream = Flux.just(new Person("Uncle Sam", 1), new Person("Oncle Pierre", 2)); - - Tuple3, Flux, Flux> result = multiOutputFunction.apply(personStream); - - result.getT1().subscribe(v -> System.out.println("=> 1: " + v)); - result.getT2().subscribe(v -> System.out.println("=> 2: " + v)); - result.getT3().subscribe(v -> System.out.println("=> 3: " + v)); - } - - @SuppressWarnings("rawtypes") - @Test - public void SCF_GH_409ConfigurationTests() { - FunctionCatalog catalog = this.configureCatalog(SCF_GH_409ConfigurationAsSupplier.class); - assertThat((Function) catalog.lookup("")).isNull(); - - catalog = this.configureCatalog(SCF_GH_409ConfigurationAsFunction.class); - assertThat((Function) catalog.lookup("")).isNull(); - } - - @Test - public void pojoFunctionAsJson() { - FunctionCatalog catalog = this.configureCatalog(); - Function uppercasePerson = catalog.lookup("uppercasePerson"); - - Person person = uppercasePerson.apply("{\"name\":\"bill\",\"id\":2}"); - assertThat(person.getName()).isEqualTo("BILL"); - } - - @Test - public void SCF_GH_429ConfigurationTests() throws Exception { - FunctionCatalog catalog = this.configureCatalog(MyFunction.class); - FunctionInvocationWrapper function = catalog.lookup("beanFactoryAwareFunctionRegistryTests.MyFunction"); - assertThat(function).isNotNull(); - Field f = ReflectionUtils.findField(FunctionInvocationWrapper.class, "composed"); - f.setAccessible(true); - boolean composed = (boolean) f.get(function); - assertThat(composed).isFalse(); - } - - @SuppressWarnings("unchecked") - @Test - @Disabled - public void byteArrayNoSpecialHandling() throws Exception { - FunctionCatalog catalog = this.configureCatalog(ByteArrayFunction.class); - FunctionInvocationWrapper function = catalog.lookup("beanFactoryAwareFunctionRegistryTests.ByteArrayFunction", "application/json"); - assertThat(function).isNotNull(); - Message result = (Message) function.apply(MessageBuilder.withPayload("hello".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "application/octet-stream").build()); - - System.out.println(new String(result.getPayload())); - - assertThat(result.getPayload()).isEqualTo("\"b2xsZWg=\"".getBytes()); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testMultipleValuesInOutputHandling() throws Exception { - FunctionCatalog catalog = this.configureCatalog(CollectionOutConfiguration.class); - FunctionInvocationWrapper function = catalog.lookup("parseToList", "application/json"); - assertThat(function).isNotNull(); - Object result = function.apply(MessageBuilder.withPayload("1,2,3".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build()); - assertThat(result instanceof Message).isTrue(); - byte[] payload = ((Message) result).getPayload(); - JsonMapper mapper = this.context.getBean(JsonMapper.class); - List resultList = mapper.fromJson(payload, List.class); - assertThat(resultList.size()).isEqualTo(3); - assertThat(resultList.get(0)).isEqualTo("1"); - assertThat(resultList.get(1)).isEqualTo("2"); - - function = catalog.lookup("parseToListOfMessages", "application/json"); - assertThat(function).isNotNull(); - result = function.apply(MessageBuilder.withPayload("1,2,3".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build()); - assertThat(result instanceof List).isTrue(); - } - - /** - * The following two tests test the fallback mechanism when an accept header has several values. - * The function produces Integer, which cannot be serialized by the default converter supporting text/plain - * (StringMessageConverter) but can by the one supporting application/json, which comes second. - */ - //@Test - public void testMultipleOrderedAcceptValues() throws Exception { - FunctionCatalog catalog = this.configureCatalog(MultipleOrderedAcceptValuesConfiguration.class); - Function> function = catalog.lookup("beanFactoryAwareFunctionRegistryTests.MultipleOrderedAcceptValuesConfiguration", "text/plain,application/json"); - assertThat(function).isNotNull(); - Message result = function.apply("hello"); - assertThat(result.getPayload()).isEqualTo("5".getBytes("UTF-8")); - } - - @Test - public void testMultipleOrderedAcceptValuesMessageOutput() throws Exception { - FunctionCatalog catalog = this.configureCatalog(MultipleOrderedAcceptValuesAsMessageOutputConfiguration.class); - Function> function = catalog.lookup( - "beanFactoryAwareFunctionRegistryTests.MultipleOrderedAcceptValuesAsMessageOutputConfiguration", - "text/plain,application/json"); - assertThat(function).isNotNull(); - Message result = function.apply("hello"); - assertThat(result.getPayload()).isEqualTo("5".getBytes("UTF-8")); - } - - @SuppressWarnings("unchecked") - @Test - public void testSerializationWithCompatibleWildcardSubtypeAcceptHeader() { - FunctionCatalog catalog = this.configureCatalog(NegotiatingMessageConverterConfiguration.class); - FunctionInvocationWrapper function = catalog.lookup("echo", "text/*"); - - Message> tupleResult = (Message>) function.apply(MessageBuilder - .withPayload(Tuples.of("bonjour", "monde")) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeType.valueOf("text/csv")) - .build() - ); - - assertThat(tupleResult.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeType.valueOf("text/csv")); - assertThat(tupleResult.getHeaders().get("accept")).isNull(); - - Message dateResult = (Message) function.apply(MessageBuilder - .withPayload(123) - .setHeader(MessageHeaders.CONTENT_TYPE, MimeType.valueOf("text/integer")) - .build() - ); - - assertThat(dateResult.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeType.valueOf("text/integer")); - assertThat(dateResult.getHeaders().get("accept")).isNull(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testWithComplexHierarchyAndTypeConversion() { - FunctionCatalog catalog = this.configureCatalog(ReactiveFunctionImpl.class); - Function f = catalog.lookup(""); - assertThat(f.apply(new GenericMessage("23")).blockFirst()).isEqualTo(23); - assertThat(f.apply(Flux.just("25")).blockFirst()).isEqualTo(25); - assertThat(f.apply(Flux.just(25)).blockFirst()).isEqualTo(25); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testRegisteringWithTypeThatDoesNotMatchDiscoveredType() { - FunctionCatalog catalog = this.configureCatalog(EmptyConfiguration.class); - Function func = catalog.lookup("func"); - assertThat(func).isNull(); - FunctionRegistry registry = (FunctionRegistry) catalog; - try { - FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "a") - .type(FunctionTypeUtils.functionType(Integer.class, String.class)); - registry.register(registration); - fail(); - } - catch (IllegalArgumentException e) { - // good as we expect it to fail - } - // - try { - FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "b") - .type(FunctionTypeUtils.functionType(String.class, Integer.class)); - registry.register(registration); - fail(); - } - catch (IllegalArgumentException e) { - // good as we expect it to fail - } - // - FunctionRegistration c = new FunctionRegistration(new MyFunction(), "c") - .type(FunctionTypeUtils.functionType(String.class, String.class)); - registry.register(c); - // - FunctionRegistration d = new FunctionRegistration(new RawFunction(), "d") - .type(FunctionTypeUtils.functionType(Person.class, String.class)); - registry.register(d); - // - FunctionRegistration e = new FunctionRegistration(new RawFunction(), "e") - .type(FunctionTypeUtils.functionType(Object.class, Object.class)); - registry.register(e); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testNoConversionOnInputMapIfInputIsMap() { - FunctionCatalog catalog = this.configureCatalog(); - Function f = catalog.lookup("maptopojo"); - Person p = new Person("John", 123); - Map map = new HashMap<>(); - map.put("person", p); - map.put("foo", "foo"); - assertThat(f.apply(map)).isInstanceOf(Person.class); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testValueWrappedInMessageIfNecessary() { - FunctionCatalog catalog = this.configureCatalog(PojoToMessageFunctionCompositionConfiguration.class); - Function f = catalog.lookup("uppercase|echo"); - assertThat(f.apply("hello")).isEqualTo("HELLO"); - f = catalog.lookup("toJson|uppercasePerson"); - assertThat(f.apply("Bubbles")).isEqualTo("BUBBLES"); - } - - @Test - public void testSupplierConsumerAsRunnable() { - FunctionCatalog catalog = this.configureCatalog(SampleFunctionConfiguration.class); - Runnable f = catalog.lookup("numberword|imperativeConsumer"); - f.run(); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testWrappedWithAroundAdviseConfiguration() { - FunctionCatalog catalog = this.configureCatalog(WrappedWithAroundAdviseConfiguration.class); - Function f = catalog.lookup("uppercase"); - Message result = (Message) f.apply(new GenericMessage("hello")); - assertThat(result.getHeaders().get("before")).isEqualTo("foo"); - assertThat(result.getHeaders().get("after")).isEqualTo("bar"); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testEachElementInFluxIsProcessed() { - FunctionCatalog catalog = this.configureCatalog(SampleFunctionConfiguration.class); - Function f = catalog.lookup("uppercasePerson"); - - Flux flux = Flux.just("{\"id\":1, \"name\":\"oleg\"}", "{\"id\":2, \"name\":\"seva\"}"); - Flux result = (Flux) f.apply(flux); - - List list = (List) result.collectList().block(); - assertThat(list.size()).isEqualTo(2); - assertThat(list.get(0).name).isEqualTo("OLEG"); - assertThat(list.get(1).name).isEqualTo("SEVA"); - - - - result = (Flux) f.apply(new GenericMessage("[{\"id\":1, \"name\":\"oleg\"}, {\"id\":2, \"name\":\"seva\"}]")); - list = (List) result.collectList().block(); - assertThat(list.size()).isEqualTo(2); - assertThat(list.get(0).name).isEqualTo("OLEG"); - assertThat(list.get(1).name).isEqualTo("SEVA"); - } - - @Test - public void testGH_608() { - ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - - Consumer> consumer = catalog.lookup("reactivePojoConsumer"); - consumer.accept(Flux.just("{\"name\":\"Ricky\"}")); - SampleFunctionConfiguration config = context.getBean(SampleFunctionConfiguration.class); - assertThat(((Person) config.consumerInputRef.get()).getName()).isEqualTo("Ricky"); - } - - @Test - public void testGH_611() { - FunctionCatalog catalog = this.configureCatalog(NegotiatingMessageConverterConfiguration.class); - Supplier> f = catalog.lookup("supplier", "text/*"); - assertThat(f.get().getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeTypeUtils.parseMimeType("text/*")); - } - - @Test - public void testGH_608_C() { - ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - - String productJson = "{\"key\":\"someKey\",\"data\": {\"name\":\"bike\"}}"; - - FunctionInvocationWrapper function = catalog.lookup("echoGenericObjectFlux", "application/json"); - Message result = ((Flux>) function.apply(productJson)).blockFirst(); - assertThat(new String(result.getPayload())).isEqualTo("\"bike\""); - } - - @Test - public void testGH_609() { - FunctionCatalog catalog = this.configureCatalog(SampleFunctionConfiguration.class); - Function, Publisher> f = catalog.lookup("monoToMono"); - Mono result = (Mono) f.apply(Mono.just("hello")); - assertThat(result.block()).isEqualTo("hello"); - - result = (Mono) f.apply(Flux.just("hello")); - assertThat(result.block()).isEqualTo("hello"); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testGH_635() throws Exception { - FunctionCatalog catalog = this.configureCatalog(SCF_GH_635ConfigurationAsFunction.class); - Function lmFunction = catalog.lookup("emptyMessageList", "application/json"); - List> emptyListOfMessages = (List>) lmFunction.apply(MessageBuilder.withPayload("hello").build()); - assertThat(emptyListOfMessages).isEmpty(); - emptyListOfMessages = (List>) lmFunction.apply("hello"); - assertThat(emptyListOfMessages).isEmpty(); - - JsonMapper mapper = this.context.getBean(JsonMapper.class); - Function lsFunction = catalog.lookup("emptyStringList", "application/json"); - Message emptyListOfString = (Message) lsFunction.apply(MessageBuilder.withPayload("hello").build()); - List resultList = mapper.fromJson(emptyListOfString.getPayload(), List.class); - assertThat(resultList).isEmpty(); - emptyListOfString = (Message) lsFunction.apply("hello"); - resultList = mapper.fromJson(emptyListOfString.getPayload(), List.class); - assertThat(resultList).isEmpty(); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testGH_768() throws Exception { - FunctionCatalog catalog = this.configureCatalog(SCF_GH_768ConfigurationAsFunction.class); - Function function = catalog.lookup("echo"); - - JsonMapper mapper = this.context.getBean(JsonMapper.class); - String date = mapper.toString(new Date()); - String result = (String) function.apply("{\"date\":" + date + "}"); - assertThat(result).startsWith("{date="); - } - - @Test - public void test_791() { - try (ConfigurableApplicationContext ac = new SpringApplicationBuilder(InputHeaderPropagationConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true")) { - FunctionCatalog catalog = ac.getBean(FunctionCatalog.class); - - Function, Message> uppercase = catalog.lookup("uppercase", "application/json"); - Message result = uppercase.apply(MessageBuilder.withPayload("bob").setHeader("foo", "bar").build()); - assertThat(result.getHeaders()).doesNotContainKey("foo"); - } - try (ConfigurableApplicationContext ac = new SpringApplicationBuilder(InputHeaderPropagationConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true", - "--spring.cloud.function.configuration.uppercase.copy-input-headers=true")) { - FunctionCatalog catalog = ac.getBean(FunctionCatalog.class); - - Function, Message> uppercase = catalog.lookup("uppercase", "application/json"); - Message result = uppercase.apply(MessageBuilder.withPayload("bob").setHeader("foo", "bar").build()); - assertThat(result.getHeaders()).containsKey("foo"); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testArrayPayloadOnFluxFunction() throws Exception { - FunctionCatalog catalog = this.configureCatalog(SampleFunctionConfiguration.class); - FunctionInvocationWrapper lmFunction = catalog.lookup("uppercaseFlux", "application/json"); - lmFunction.setSkipOutputConversion(true); - List list = new ArrayList<>(); - list.add("Ricky"); - list.add("Julien"); - list.add("Bubbles"); - Publisher p = (Publisher) lmFunction.apply(MessageBuilder.withPayload(list).setHeader(MessageHeaders.CONTENT_TYPE, "application/json").build()); - List result = new ArrayList<>(); - for (Object value : Flux.from(p).toIterable()) { - result.add(value); - } - assertThat(result.size()).isEqualTo(3); - } - - @Test - // see GH-707 - public void testConcurrencyOnLookup() throws Exception { - AtomicInteger counter = new AtomicInteger(); - - ExecutorService executor = Executors.newFixedThreadPool(10); - for (int i = 0; i < 10; i++) { - FunctionCatalog catalog = this.configureCatalog(SampleFunctionConfiguration.class); - for (int y = 0; y < 10; y++) { - executor.execute(() -> { - assertThat((FunctionInvocationWrapper) catalog.lookup("uppercase|reverse", "application/json")).isNotNull(); - counter.incrementAndGet(); - }); - } - } - - executor.shutdown(); - executor.awaitTermination(10000, TimeUnit.MILLISECONDS); - assertThat(counter.get()).isEqualTo(100); - } - - @EnableAutoConfiguration - public static class PojoToMessageFunctionCompositionConfiguration { - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - @Bean - public Function, String> echo() { - return v -> v.getPayload(); - } - - @Bean - public Function toJson() { - return v -> "{\"id\":1, \"name\":\"" + v + "\"}"; - } - - @Bean - public Function, String> uppercasePerson() { - return v -> v.getPayload().getName().toUpperCase(); - } - } - - @EnableAutoConfiguration - @Configuration - public static class JsonNodeConfiguration { - @Bean - public Function, String> messageAsJsonNode() { - return v -> { - return v.getPayload().toString(); - }; - } - - @Bean - public Function asJsonNode() { - return v -> { - return v.toString(); - }; - } - } - - @EnableAutoConfiguration - public static class EmptyConfiguration { - - } - - public interface ReactiveFunction extends Function, Flux> { - - } - - @Component - @EnableAutoConfiguration - public static class ReactiveFunctionImpl implements ReactiveFunction { - @Override - public Flux apply(Flux inFlux) { - return inFlux.map(v -> Integer.parseInt(v)); - } - } - - @EnableAutoConfiguration - public static class CollectionOutConfiguration { - - @Bean - public Function> parseToList() { - return v -> Arrays.asList(v.split(",")); - } - - @Bean - public Function>> parseToListOfMessages() { - return v -> { - List> list = Arrays.asList(v.split(",")).stream() - .map(value -> MessageBuilder.withPayload(value).build()).collect(Collectors.toList()); - return list; - }; - } - } - - @EnableAutoConfiguration - public static class NegotiatingMessageConverterConfiguration { - - @Bean - public Supplier supplier() { - return () -> 123; - } - - @Bean - public Function echo() { - return v -> v; - } - - @Bean - public MessageConverter messageConverterA() { - return new ConverterA(); - } - - @Bean - public MessageConverter messageConverterB() { - return new ConverterB(); - } - - @Bean - public MessageConverter messageConverterC() { - return new ConverterC(); - } - - public static class ConverterC extends ConverterA { - ConverterC() { - super("text/*"); - } - - @Override - protected Object convertFromInternal( - Message message, Class targetClass, @Nullable Object conversionHint) { - return message.getPayload().toString(); - } - - @Override - public Object convertToInternal(Object rawPayload, MessageHeaders headers, Object conversionHint) { - return rawPayload; - } - - @Override - protected boolean canConvertFrom(Message message, @Nullable Class targetClass) { - return supportsMimeType(message.getHeaders()) && Integer.class.isAssignableFrom(targetClass) - && message.getPayload() instanceof Integer; - } - - @Override - protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) { - return payload instanceof Integer; - } - } - - public static class ConverterB extends ConverterA { - ConverterB() { - super("text/integer"); - } - - @Override - protected Object convertFromInternal( - Message message, Class targetClass, @Nullable Object conversionHint) { - return message.getPayload().toString(); - } - - @Override - public Object convertToInternal(Object rawPayload, MessageHeaders headers, Object conversionHint) { - return Integer.parseInt((String) rawPayload); - } - - @Override - protected boolean canConvertFrom(Message message, @Nullable Class targetClass) { - return supportsMimeType(message.getHeaders()) && String.class.isAssignableFrom(targetClass) - && message.getPayload() instanceof Integer; - } - - @Override - protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) { - return payload instanceof String; - } - } - - private static class ConverterA extends AbstractMessageConverter { - - ConverterA() { - this("text/csv"); - } - - ConverterA(String mimeType) { - super(singletonList(MimeType.valueOf(mimeType))); - } - - @SuppressWarnings("unchecked") - @Override - protected Object convertFromInternal( - Message message, Class targetClass, @Nullable Object conversionHint) { - Tuple2 payload = (Tuple2) message.getPayload(); - - String convertedPayload = payload.getT1() + "," + payload.getT2(); - return convertedPayload; - } - - @Override - public Object convertToInternal(Object rawPayload, MessageHeaders headers, Object conversionHint) { - return Tuples.fromArray(((String) rawPayload).split(",")); - } - - @Override - protected boolean canConvertFrom(Message message, @Nullable Class targetClass) { - return supportsMimeType(message.getHeaders()) && String.class.isAssignableFrom(targetClass) - && message.getPayload() instanceof Tuple2; - } - - @Override - protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) { - return payload instanceof String && ((String) payload).split(",").length == 2; - } - - @Override - protected boolean supports(Class clazz) { - throw new UnsupportedOperationException(); - } - } - } - - @EnableAutoConfiguration - @Configuration - protected static class WrappedWithAroundAdviseConfiguration { - @Bean - public Function, Message> uppercase() { - return v -> MessageBuilder.withPayload(v.getPayload().toUpperCase()).copyHeaders(v.getHeaders()).build(); - } - - @Bean - @ConditionalOnMissingBean - public FunctionAroundWrapper wrapper() { - return new FunctionAroundWrapper() { - - @SuppressWarnings("unchecked") - @Override - protected Object doApply(Object input, FunctionInvocationWrapper targetFunction) { - // in this test we know input is a Message - Message mInput = (Message) input; - MessageBuilder.fromMessage(mInput).setHeader("before", "foo").build(); - Message result = (Message) targetFunction.apply(MessageBuilder.fromMessage(mInput).setHeader("before", "foo").build()); - return MessageBuilder.fromMessage(result).setHeader("after", "bar").build(); - } - }; - } - } - - @EnableAutoConfiguration - @Configuration - protected static class InputHeaderPropagationConfiguration { - - @Bean - public Function uppercase() { - return x -> x.toUpperCase(); - } - } - - @EnableAutoConfiguration - @Configuration - protected static class SampleFunctionConfiguration { - - AtomicReference consumerInputRef = new AtomicReference<>(); - - @Bean - public Function>>, Flux> echoGenericObjectFlux() { - return x -> x.map(eventMessage -> eventMessage.getPayload().getData().getName()); - } - - @Bean - public Function uppercasePerson() { - return person -> { - return new Person(person.getName().toUpperCase(), person.getId()); - }; - } - - @Bean - public Supplier numberword() { - return () -> "one"; - } - - @Bean - public BiFunction biFuncUpperCase() { - return (p, h) -> { - return p.toUpperCase(); - }; - } - - @Bean - public Function, Person> maptopojo() { - return map -> { - Person person = (Person) map.get("person"); - return person; - }; - } - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - @Bean - public Function, Message> uppercaseMessage() { - return message -> { - Message result = MessageBuilder.fromMessage(message) - .removeHeader("foo").setHeader("bar", "bar").build(); - return result; - }; - } - - @Bean - public Function consumerFunction() { - return v -> { - System.out.println("Value: " + v); - return null; - }; - } - - @Bean - public Function, Flux> uppercaseFlux() { - return flux -> flux.map(v -> v.toUpperCase()); - } - - @Bean - public Function voidInputFunction() { - return v -> "voidInputFunction"; - } - - @Bean - public Function, Flux> voidInputFunctionReactive() { - return flux -> Flux.just("voidInputFunctionReactive"); - } - - @Bean - public Function, Flux> voidInputFunctionReactive2() { - return mono -> Flux.just("voidInputFunctionReactive2"); - } - - @Bean - public Function reverse() { - return value -> new StringBuilder(value).reverse().toString(); - } - - @Bean - public Function, Flux> reverseFlux() { - return flux -> flux.map(value -> { - return new StringBuilder(value).reverse().toString(); - }); - } - - - @Bean - public Function, Mono> monoVoidToMonoVoid() { - return mono -> mono.doOnSuccess(v -> System.out.println("HELLO")); - } - - // ============= MESSAGE-IN and MESSAGE-OUT functions ============ - - // ============= MULTI-INPUT and MULTI-OUTPUT functions ============ - - @Bean - public Function, Flux>, Flux> multiInputSingleOutputViaReactiveTuple() { - return tuple -> { - Flux stringStream = tuple.getT1(); - Flux intStream = tuple.getT2(); - return Flux.zip(stringStream, intStream, (string, integer) -> string + "-" + integer); - }; - } - //======== - - // MULTI-OUTPUT - @Bean - public Function, Tuple3, Flux, Flux>> multiOutputAsTuple() { - return flux -> { - Flux pubSubFlux = flux.publish().autoConnect(3); - Flux nameFlux = pubSubFlux.map(person -> person.getName()); - Flux idFlux = pubSubFlux.map(person -> person.getId()); - return Tuples.of(pubSubFlux, nameFlux, idFlux); - }; - } - - public Function, Flux>> multiOutputAsTuple2() { - return null; - } - //======== - - @Bean - public Function, Mono> monoToMonoVoid() { - return null; - } - - @Bean - public Function, Mono> monoToMono() { - return mono -> mono; - } - - @Bean - public Function, Flux> fluxVoidToFluxVoid() { - return null; - } - - @Bean - public Function, Flux> monoToFluxVoid() { - return null; - } - - @Bean - public Function, Mono> fluxToMonoVoid() { - return null; - } - - @Bean - public Function, Flux> monoToFlux() { - return null; - } - - @Bean - public Function, Mono> fluxToMono() { - return null; - } - - @Bean - public Supplier imperativeSupplier() { - return null; - } - - @Bean - public Supplier> reactiveSupplier() { - return null; - } - - @Bean - public Consumer imperativeConsumer() { - return System.out::println; - } - - @Bean - // Perhaps it should not be allowed. Recommend Function> - public Consumer> reactiveConsumer() { - return null; - } - - @Bean - // Perhaps it should not be allowed. Recommend Function> - public Consumer> reactivePojoConsumer() { - return flux -> flux.subscribe(v -> consumerInputRef.set(v)); - } - } - - @EnableAutoConfiguration - public static class SCF_GH_409ConfigurationAsSupplier { - - @Bean - public Serializable blah() { - return new Foo(); - } - - private static class Foo implements Supplier, Serializable { - - @Override - public Object get() { - // TODO Auto-generated method stub - return null; - } - - } - } - - @EnableAutoConfiguration - public static class SCF_GH_409ConfigurationAsFunction { - - @Bean - public Serializable blah() { - return new Foo(); - } - - private static class Foo implements Function, Serializable { - - @Override - public Object apply(Object t) { - // TODO Auto-generated method stub - return null; - } - } - } - - @EnableAutoConfiguration - public static class SCF_GH_635ConfigurationAsFunction { - - @Bean - public Function>> emptyMessageList() { - return input -> Collections.emptyList(); - } - - @Bean - public Function> emptyStringList() { - return input -> Collections.emptyList(); - } - } - - @EnableAutoConfiguration - public static class SCF_GH_768ConfigurationAsFunction { - @Bean - public Function, String> echoToString() { - return data -> { - for (Entry dataEntry : data.entrySet()) { - assertThat(dataEntry.getValue()).isInstanceOf(Date.class); // would fail if value would not be converted to Person - } - return data.toString(); - }; - } - } - - public static class Person { - private String name; - private int id; - public Person() { - - } - public Person(String name, int id) { - this.name = name; - this.id = id; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public int getId() { - return id; - } - public void setId(int id) { - this.id = id; - } - @Override - public String toString() { - return "Person: " + name + "/" + id; - } - } - - @EnableAutoConfiguration - @Configuration - @Component - public static class MyFunction implements Function { - - @Override - public String apply(String t) { - return t; - } - - } - - public static class RawFunction implements Function { - - @Override - public Object apply(Object t) { - return t; - } - - } - - @EnableAutoConfiguration - @Configuration - @Component - public static class ByteArrayFunction implements Function { - - @Override - public byte[] apply(byte[] bytes) { - byte[] result = new byte[bytes.length]; - for (int i = 0; i < bytes.length; i++) { - result[i] = bytes[bytes.length - i - 1]; - } - return result; - } - } - - @EnableAutoConfiguration - @Configuration - @Component - public static class MultipleOrderedAcceptValuesConfiguration implements Function { - - @Override - public Integer apply(String t) { - return t.length(); - } - } - - @EnableAutoConfiguration - @Configuration - @Component - public static class MultipleOrderedAcceptValuesAsMessageOutputConfiguration implements Function> { - - @Override - public Message apply(String t) { - return MessageBuilder.withPayload(t.length()).build(); - } - - } - - @EnableAutoConfiguration - @Configuration - public static class ComplexTypeFunctionConfiguration { - @Bean - public Function, String> function() { - return v -> v.getData().getName().toUpperCase(); - } - } - - private static class Product { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } - - private static class Event { - - private K key; - - public K getKey() { - return key; - } - - public void setKey(K key) { - this.key = key; - } - - private V data; - - public V getData() { - return data; - } - - public void setData(V data) { - this.data = data; - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwarePojoFunctionRegistryTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwarePojoFunctionRegistryTests.java deleted file mode 100644 index accc4c5ba..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwarePojoFunctionRegistryTests.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class BeanFactoryAwarePojoFunctionRegistryTests { - - private FunctionCatalog configureCatalog() { - ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .run("--logging.level.org.springframework.cloud.function=DEBUG"); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - return catalog; - } - - @Test - public void testWithPojoFunctionImplementingFunction() { - FunctionCatalog catalog = this.configureCatalog(); - -// MyFunction f1 = catalog.lookup("myFunction"); -// assertThat(f1.uppercase("foo")).isEqualTo("FOO"); - - Function f2 = catalog.lookup("myFunction"); - assertThat(f2.apply("foo")).isEqualTo("FOO"); - - Function f2conversion = catalog.lookup("myFunction"); - assertThat(f2conversion.apply(123)).isEqualTo("123"); - - Function, String> f2message = catalog.lookup("myFunction"); - assertThat(f2message.apply(MessageBuilder.withPayload("message").build())).isEqualTo("MESSAGE"); - - Function, Flux> f3 = catalog.lookup("myFunction"); - assertThat(f3.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); - - Function, Message> f2messageReturned = catalog.lookup("myFunction", "application/json"); - assertThat(new String(f2messageReturned.apply(MessageBuilder.withPayload("message").build()).getPayload())).isEqualTo("\"MESSAGE\""); - } - - @Test - public void testWithPojoFunction() { - FunctionCatalog catalog = this.configureCatalog(); - -// MyFunctionLike f1 = catalog.lookup("myFunctionLike"); -// assertThat(f1.uppercase("foo")).isEqualTo("FOO"); - - Function f2 = catalog.lookup("myFunctionLike"); - assertThat(f2.apply("foo")).isEqualTo("FOO"); - - Function f2conversion = catalog.lookup("myFunctionLike"); - assertThat(f2conversion.apply(123)).isEqualTo("123"); - - Function, String> f2message = catalog.lookup("myFunctionLike"); - assertThat(f2message.apply(MessageBuilder.withPayload("message").build())).isEqualTo("MESSAGE"); - - Function, Flux> f3 = catalog.lookup("myFunctionLike"); - assertThat(f3.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); - - Function, Message> f2messageReturned = catalog.lookup("myFunctionLike", "application/json"); - assertThat(new String(f2messageReturned.apply(MessageBuilder.withPayload("message").build()).getPayload())).isEqualTo("\"MESSAGE\""); - } - - @Test - public void testWithPojoFunctionComposition() { - FunctionCatalog catalog = this.configureCatalog(); - Function f1 = catalog.lookup("myFunction|myFunctionLike|func"); - assertThat(f1.apply("foo")).isEqualTo("FOO"); - } - - - @EnableAutoConfiguration - @Configuration - protected static class SampleFunctionConfiguration { - - @Bean - public MyFunction myFunction() { - return new MyFunction(); - } - - @Bean - public MyFunctionLike myFunctionLike() { - return new MyFunctionLike(); - } - - @Bean - public Function func() { - return v -> v; - } - } - - // POJO Function that implements Function - private static class MyFunction implements Function { - public String uppercase(String value) { - return value.toUpperCase(); - } - - @Override - public String apply(String t) { - return this.uppercase(t); - } - } - - // POJO Function - public static class MyFunctionLike { - public String uppercase(String value) { - return value.toUpperCase(); - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java deleted file mode 100644 index ee1dfb29b..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - - -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuple3; - -import org.springframework.core.MethodParameter; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.messaging.Message; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * - */ -@SuppressWarnings("unused") -public class FunctionTypeUtilsTests { - - @Test - public void testFunctionTypeFrom() throws Exception { - Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(SimpleConsumer.class); - assertThat(type).isInstanceOf(ParameterizedType.class); - Type wrapperType = ((ParameterizedType) type).getActualTypeArguments()[0]; - assertThat(wrapperType).isInstanceOf(ParameterizedType.class); - assertThat(wrapperType.getTypeName()).contains("Flux"); - - Type innerWrapperType = ((ParameterizedType) wrapperType).getActualTypeArguments()[0]; - assertThat(innerWrapperType).isInstanceOf(ParameterizedType.class); - assertThat(innerWrapperType.getTypeName()).contains("Message"); - - Type targetType = ((ParameterizedType) innerWrapperType).getActualTypeArguments()[0]; - assertThat(targetType).isEqualTo(String.class); - System.out.println(); - } - - @Test - public void testFunctionTypeByClassDiscovery() { - Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(Function.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(Object.class); - - type = FunctionTypeUtils.discoverFunctionTypeFromClass(MessageFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))).isAssignableFrom(String.class); - - type = FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageFunction.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))).isAssignableFrom(String.class); - - type = FunctionTypeUtils.discoverFunctionTypeFromClass(MessageConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); - - type = FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageConsumer.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); - } - - @Test - public void testWithComplexHierarchy() { - Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(ReactiveFunctionImpl.class); - assertThat(String.class).isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))); - assertThat(Integer.class).isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))); - } - - @Test - public void testIsTypeCollection() { - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference() { }.getType())).isFalse(); - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>() { }.getType())).isTrue(); - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>>() { }.getType())).isTrue(); - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>>>() { }.getType())).isTrue(); - assertThat(FunctionTypeUtils.isTypeCollection(new ParameterizedTypeReference>>>() { }.getType())).isFalse(); - } - - @Test - public void testNoNpeFromIsMessage() { - FunctionTypeUtilsTests testService = new FunctionTypeUtilsTests<>(); - - Method methodUnderTest = - ReflectionUtils.findMethod(testService.getClass(), "notAMessageMethod", AtomicReference.class); - MethodParameter methodParameter = MethodParameter.forExecutable(methodUnderTest, 0); - - assertThat(FunctionTypeUtils.isMessage(methodParameter.getGenericParameterType())).isFalse(); - } - - void notAMessageMethod(AtomicReference payload) { - - } - - private static Function function() { - return null; - } - - @SuppressWarnings("rawtypes") - private static Function typelessFunction() { - return null; - } - - private static Function, Tuple3> multiInputOutputFunction() { - return null; - } - - private static Function, Mono>, - Tuple3, Flux, Mono>> multiInputOutputPublisherFunction() { - return null; - } - - private static Function>, Mono>, - Tuple3>, Flux, Mono>> multiInputOutputPublisherFunctionComplexTypes() { - return null; - } - - private static Consumer consumer() { - return null; - } - - private static Consumer> multiInputConsumer() { - return null; - } - - @SuppressWarnings("rawtypes") - private static Consumer typelessConsumer() { - return null; - } - - private static Supplier supplier() { - return null; - } - - @SuppressWarnings("rawtypes") - private static Supplier typelessSupplier() { - return null; - } - - private static Supplier> multiOutputSupplier() { - return null; - } - - private Type getReturnType(String methodName) throws Exception { - return FunctionTypeUtilsTests.class.getDeclaredMethod(methodName).getGenericReturnType(); - } - - //============ - - private interface MessageFunction extends Function, Message> { - - } - - private interface MyMessageFunction extends MessageFunction { - - } - - private interface MessageConsumer extends Consumer> { - - } - - private interface MyMessageConsumer extends MessageConsumer { - - } - - public static class SimpleConsumer implements Consumer>> { - @Override - public void accept(Flux> messageFlux) { - } - } - - public interface ReactiveFunction extends Function, Flux> { - - } - - public static class ReactiveFunctionImpl implements ReactiveFunction { - - @Override - public Flux apply(Flux inFlux) { - return inFlux.map(v -> Integer.parseInt(v)); - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java deleted file mode 100644 index 4314ee669..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java +++ /dev/null @@ -1,839 +0,0 @@ -/* - * Copyright 2012-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.catalog; - -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.Gson; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.HybridFunctionalRegistrationTests.UppercaseFunction; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.config.JsonMessageConverter; -import org.springframework.cloud.function.context.config.SmartCompositeMessageConverter; -import org.springframework.cloud.function.json.GsonMapper; -import org.springframework.cloud.function.json.JacksonMapper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.core.ResolvableType; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.converter.AbstractMessageConverter; -import org.springframework.messaging.converter.ByteArrayMessageConverter; -import org.springframework.messaging.converter.CompositeMessageConverter; -import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.converter.StringMessageConverter; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeType; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Oleg Zhurakousky - * @author Soby Chacko - * @author Chris Bono - */ -public class SimpleFunctionRegistryTests { - - private CompositeMessageConverter messageConverter; - - private ConversionService conversionService; - - @BeforeEach - public void before() { - List messageConverters = new ArrayList<>(); - JsonMapper jsonMapper = new GsonMapper(new Gson()); - messageConverters.add(new JsonMessageConverter(jsonMapper)); - messageConverters.add(new ByteArrayMessageConverter()); - messageConverters.add(new StringMessageConverter()); - this.messageConverter = new SmartCompositeMessageConverter(messageConverters); - - this.conversionService = new DefaultConversionService(); - } - - @SuppressWarnings("rawtypes") - @Test - public void concurrencyRegistrationTest() throws Exception { - Echo function = new Echo(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(Echo.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - ExecutorService executor = Executors.newCachedThreadPool(); - for (int i = 0; i < 1000; i++) { - executor.execute(() -> { - catalog.register(registration); - }); - } - Thread.sleep(1000); - Field frField = ReflectionUtils.findField(catalog.getClass(), "functionRegistrations"); - frField.setAccessible(true); - Collection c = (Collection) frField.get(catalog); - assertThat(c.size()).isEqualTo(1); - } - - @Test - public void testCachingOfFunction() { - Echo function = new Echo(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(Echo.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - - FunctionInvocationWrapper instanceA = catalog.lookup("echo", "application/json"); - FunctionInvocationWrapper instanceb = catalog.lookup("echo", "text/plain"); - FunctionInvocationWrapper instanceC = catalog.lookup("echo", "foo/bar"); - - assertThat(instanceA).isSameAs(instanceb).isSameAs(instanceC); - } - - @Test - public void testNoCachingOfFunction() { - Echo function = new Echo(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(Echo.class); - registration.getProperties().put("singleton", "false"); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - - FunctionInvocationWrapper instanceA = catalog.lookup("echo", "application/json"); - FunctionInvocationWrapper instanceb = catalog.lookup("echo", "text/plain"); - FunctionInvocationWrapper instanceC = catalog.lookup("echo", "foo/bar"); - - assertThat(instanceA).isNotSameAs(instanceb).isNotSameAs(instanceC); - } - - @Test - public void testSCF768() { - ResolvableType map = ResolvableType.forClassWithGenerics(Map.class, String.class, Person.class); - Type functionType = ResolvableType.forClassWithGenerics(Function.class, map, ResolvableType.forClass(String.class)).getType(); - - Function, String> function = persons -> { - for (Entry entry : persons.entrySet()) { - assertThat(entry.getValue().getName()).isNotEmpty(); // would fail if value would not be converted to Person - } - return persons.toString(); - }; - - FunctionRegistration, String>> registration = new FunctionRegistration<>( - function, "echo").type(functionType); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - - FunctionInvocationWrapper lookedUpFunction = catalog.lookup("echo"); - String result = (String) lookedUpFunction.apply("{\"ricky\":{\"name\":\"ricky\"}}"); - assertThat(result).isEqualTo("{ricky=ricky}"); - } - - @Test - public void testSCF640() { - Echo function = new Echo(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(Echo.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - - FunctionInvocationWrapper lookedUpFunction = catalog.lookup("echo"); - Object result = lookedUpFunction.apply("{\"HELLO\":\"WORLD\"}"); - assertThat(result).isNotInstanceOf(Message.class); - assertThat(result).isEqualTo("{\"HELLO\":\"WORLD\"}"); - } - - @ParameterizedTest - @ValueSource(strings = {"[hello", "hello]", "[hello]"}) - void textContentTypeWithValueWrappedBracketsIsOk(String inputMessagePayloadValue) { - var catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); - catalog.register(new FunctionRegistration<>(new Echo(), "echo").type(Echo.class)); - FunctionInvocationWrapper lookedUpFunction = catalog.lookup("echo"); - var inputMessage = MessageBuilder.withPayload(inputMessagePayloadValue) - .setHeader("contentType", "text/plain") - .build(); - var functionResult = lookedUpFunction.apply(inputMessage); - assertThat(functionResult).isEqualTo(inputMessagePayloadValue); - } - - @SuppressWarnings("unchecked") - @Test - public void testSCF762() { - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - - FunctionRegistration reg1 = new FunctionRegistration<>( - new UpperCase(), "uppercase").type(UpperCase.class); - catalog.register(reg1); - // - FunctionRegistration reg2 = new FunctionRegistration<>( - new UpperCaseMessage(), "uppercaseMessage").type(UpperCaseMessage.class); - catalog.register(reg2); - // - FunctionRegistration reg3 = new FunctionRegistration<>( - new StringArrayFunction(), "stringArray").type(StringArrayFunction.class); - catalog.register(reg3); - // - FunctionRegistration reg4 = new FunctionRegistration<>( - new TypelessFunction(), "typeless").type(TypelessFunction.class); - catalog.register(reg4); - // - FunctionRegistration reg5 = new FunctionRegistration<>( - new ByteArrayFunction(), "typeless").type(ByteArrayFunction.class); - catalog.register(reg5); - // - FunctionRegistration reg6 = new FunctionRegistration<>( - new StringListFunction(), "stringList").type(StringListFunction.class); - catalog.register(reg6); - - Message collectionMessage = MessageBuilder.withPayload("[\"ricky\", \"julien\", \"bubbles\"]").build(); - Message singleValueMessage = MessageBuilder.withPayload("\"ricky\"").build(); - - FunctionInvocationWrapper lookedUpFunction = catalog.lookup("uppercase", "application/json"); - Object result = lookedUpFunction.apply(singleValueMessage); - assertThat(result).isInstanceOf(Message.class); - assertThat(((Message) result).getPayload()).isEqualTo("\"RICKY\"".getBytes()); - - result = lookedUpFunction.apply(collectionMessage); - assertThat(result).isInstanceOf(Flux.class); - List> collectionIfResults = Flux.from((Publisher>) result).collectList().block(); - assertThat(collectionIfResults.size()).isEqualTo(3); - assertThat(collectionIfResults.get(0).getPayload()).isEqualTo("\"RICKY\"".getBytes()); - assertThat(collectionIfResults.get(1).getPayload()).isEqualTo("\"JULIEN\"".getBytes()); - - lookedUpFunction = catalog.lookup("typeless", "application/json"); - result = lookedUpFunction.apply(singleValueMessage); - assertThat(result).isInstanceOf(Message.class); - assertThat(((Message) result).getPayload()).isEqualTo("\"ricky\"".getBytes()); - - result = lookedUpFunction.apply(collectionMessage); - assertThat(result).isInstanceOf(Message.class); - assertThat(((Message) result).getPayload()).isEqualTo("[\"ricky\", \"julien\", \"bubbles\"]".getBytes()); - - - lookedUpFunction = catalog.lookup("stringArray", "application/json"); - result = lookedUpFunction.apply(singleValueMessage); - assertThat(result).isInstanceOf(Message.class); - assertThat(((Message) result).getPayload()).isEqualTo("[\"ricky\"]".getBytes()); - - result = lookedUpFunction.apply(collectionMessage); - assertThat(result).isInstanceOf(Message.class); - assertThat(((Message) result).getPayload()).isEqualTo("[ricky, julien, bubbles]".getBytes()); - - - lookedUpFunction = catalog.lookup("stringList", "application/json"); - result = lookedUpFunction.apply(singleValueMessage); - assertThat(result).isInstanceOf(Message.class); - assertThat(((Message) result).getPayload()).isEqualTo("[\"ricky\"]".getBytes()); - - result = lookedUpFunction.apply(collectionMessage); - assertThat(result).isInstanceOf(Message.class); - System.out.println(new String(((Message) result).getPayload())); - assertThat(((Message) result).getPayload()).isEqualTo("[ricky, julien, bubbles]".getBytes()); - - } - - @SuppressWarnings("unchecked") - @Test - public void testSCF588() { - - UpperCase function = new UpperCase(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "foo").type(UppercaseFunction.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - - FunctionInvocationWrapper lookedUpFunction = catalog.lookup("uppercase"); - Message message = MessageBuilder.withPayload("hello") - .setHeader("lambda-runtime-aws-request-id", UUID.randomUUID()) - .build(); - Object result = lookedUpFunction.apply(message); - assertThat(result).isInstanceOf(Message.class); - assertThat(((Message) result).getPayload()).isEqualTo("HELLO"); - } - - @Test - public void testFunctionLookup() { - - TestFunction function = new TestFunction(); - FunctionRegistration registration = new FunctionRegistration<>( - function, "foo").type(TestFunction.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - - //FunctionInvocationWrapper lookedUpFunction = catalog.lookup("hello"); - FunctionInvocationWrapper lookedUpFunction = catalog.lookup("hello"); - assertThat(lookedUpFunction).isNotNull(); // because we only have one and can look it up with any name - FunctionRegistration registration2 = new FunctionRegistration<>( - function, "foo2").type(TestFunction.class); - catalog.register(registration2); - lookedUpFunction = catalog.lookup("hello"); - assertThat(lookedUpFunction).isNull(); - } - - - - @Test - public void testFunctionComposition() { - FunctionRegistration upperCaseRegistration = new FunctionRegistration<>( - new UpperCase(), "uppercase").type(UpperCase.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(upperCaseRegistration); - catalog.register(reverseRegistration); - - Function, Flux> lookedUpFunction = catalog - .lookup("uppercase|reverse"); - assertThat(lookedUpFunction).isNotNull(); - } - - @Test - @Disabled - public void testFunctionCompositionImplicit() { - FunctionRegistration wordsRegistration = new FunctionRegistration<>( - new Words(), "words").type(Words.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); - FunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(wordsRegistration); - catalog.register(reverseRegistration); - - // There's only one function, we should be able to leave that blank - Supplier lookedUpFunction = catalog.lookup("words|"); - - assertThat(lookedUpFunction).isNotNull(); - assertThat(lookedUpFunction.get()).isEqualTo("olleh"); - } - - @Test - @Disabled - public void testFunctionCompletelyImplicitComposition() { - FunctionRegistration wordsRegistration = new FunctionRegistration<>( - new Words(), "words").type(Words.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(wordsRegistration); - catalog.register(reverseRegistration); - - // There's only one function, we should be able to leave that blank - Supplier> lookedUpFunction = catalog.lookup("|"); - - assertThat(lookedUpFunction).isNotNull(); - assertThat(lookedUpFunction.get().blockFirst()).isEqualTo("olleh"); - } - - @Test - public void testFunctionCompositionExplicit() { - FunctionRegistration wordsRegistration = new FunctionRegistration<>( - new Words(), "words").type(Words.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(wordsRegistration); - catalog.register(reverseRegistration); - - Supplier lookedUpFunction = catalog.lookup("words|reverse"); - - assertThat(lookedUpFunction).isNotNull(); - assertThat(lookedUpFunction.get()).isEqualTo("olleh"); - } - - @Test - public void testFunctionCompositionWithMessages() { - FunctionRegistration upperCaseRegistration = new FunctionRegistration<>( - new UpperCaseMessage(), "uppercase") - .type(UpperCaseMessage.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new ReverseMessage(), "reverse") - .type(ReverseMessage.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(upperCaseRegistration); - catalog.register(reverseRegistration); - - Function>, Flux>> lookedUpFunction = catalog - .lookup("uppercase|reverse"); - - assertThat(lookedUpFunction).isNotNull(); - assertThat(lookedUpFunction - .apply(Flux.just(MessageBuilder.withPayload("star").build())).blockFirst() - .getPayload()).isEqualTo("RATS"); - } - - @Test - public void testFunctionCompositionMixedMessages() { - FunctionRegistration upperCaseRegistration = new FunctionRegistration<>( - new UpperCaseMessage(), "uppercase") - .type(UpperCaseMessage.class); - FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(Reverse.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(upperCaseRegistration); - catalog.register(reverseRegistration); - - Function, String> lookedUpFunction = catalog - .lookup("uppercase|reverse"); - - assertThat(lookedUpFunction).isNotNull(); - String result = lookedUpFunction.apply(MessageBuilder.withPayload("star").setHeader("foo", "bar").build()); - assertThat(result).isEqualTo("RATS"); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testReactiveFunctionMessages() { - FunctionRegistration registration = new FunctionRegistration<>(new ReactiveFunction(), "reactive") - .type(ReactiveFunction.class); - - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - - Function lookedUpFunction = catalog.lookup("reactive"); - - assertThat(lookedUpFunction).isNotNull(); - Flux> result = (Flux>) lookedUpFunction - .apply(Flux.just(MessageBuilder - .withPayload("[{\"name\":\"item1\"},{\"name\":\"item2\"}]") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") - .build() - )); - - Assertions.assertIterableEquals(result.blockFirst(), Arrays.asList("item1", "item2")); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testWithCustomMessageConverter() { - FunctionCatalog catalog = this.configureCatalog(CustomConverterConfiguration.class); - Function function = catalog.lookup("func"); - Object result = function.apply(MessageBuilder.withPayload("Jim Lahey").setHeader(MessageHeaders.CONTENT_TYPE, "text/person").build()); - assertThat(result).isEqualTo("Jim Lahey"); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void lookup() { - SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - FunctionInvocationWrapper function = functionRegistry.lookup("uppercase"); - assertThat(function).isNull(); - - Function userFunction = uppercase(); - FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "uppercase") - .type(FunctionTypeUtils.functionType(String.class, String.class)); - functionRegistry.register(functionRegistration); - - function = functionRegistry.lookup("uppercase"); - assertThat(function).isNotNull(); - } - - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void lookupDefaultName() { - SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - Function userFunction = uppercase(); - FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "uppercase") - .type(FunctionTypeUtils.functionType(String.class, String.class)); - functionRegistry.register(functionRegistration); - - FunctionInvocationWrapper function = functionRegistry.lookup(""); - assertThat(function).isNotNull(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void lookupWithCompositionFunctionAndConsumer() { - SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - - Object userFunction = uppercase(); - FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "uppercase") - .type(FunctionTypeUtils.functionType(String.class, String.class)); - functionRegistry.register(functionRegistration); - - userFunction = consumer(); - functionRegistration = new FunctionRegistration(userFunction, "consumer") - .type(ResolvableType.forClassWithGenerics(Consumer.class, Integer.class).getType()); - functionRegistry.register(functionRegistration); - - FunctionInvocationWrapper functionWrapper = functionRegistry.lookup("uppercase|consumer"); - - functionWrapper.apply("123"); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void lookupWithReactiveConsumer() { - SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - - Object userFunction = reactiveConsumer(); - - FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "reactiveConsumer") - .type(ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forClassWithGenerics(Flux.class, Integer.class)).getType()); - functionRegistry.register(functionRegistration); - - FunctionInvocationWrapper functionWrapper = functionRegistry.lookup("reactiveConsumer"); - - functionWrapper.apply("123"); - } - - @Test - public void testHeaderEnricherFunction() { - FunctionRegistration registration = - new FunctionRegistration<>(new HeaderEnricherFunction(), "headerEnricher") - .type(HeaderEnricherFunction.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - Function, Message> function = catalog.lookup("headerEnricher"); - Message message = - function.apply(MessageBuilder.withPayload("hello").setHeader("original", "originalValue") - .build()); - assertThat(message.getHeaders().get("original")).isEqualTo("newValue"); - } - - @Test - public void testReactiveMonoSupplier() { - FunctionRegistration registration = new FunctionRegistration<>(new ReactiveMonoGreeter(), - "greeter").type(ReactiveMonoGreeter.class); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - FunctionInvocationWrapper function = catalog.lookup("greeter"); - assertThat(FunctionTypeUtils.isMono(function.getOutputType())); - } - - @Test - public void testFunctionCompositionWithReactiveSupplierAndConsumer() { - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - - Object reactiveFunc = reactiveFluxSupplier(); - FunctionRegistration functionRegistration = new FunctionRegistration(reactiveFunc, "reactiveFluxSupplier") - .type(ResolvableType.forClassWithGenerics( - Supplier.class, ResolvableType.forClassWithGenerics(Flux.class, String.class)).getType()); - catalog.register(functionRegistration); - - reactiveFunc = reactiveFluxConsumer(); - functionRegistration = new FunctionRegistration(reactiveFunc, "reactiveFluxConsumer") - .type(ResolvableType.forClassWithGenerics( - Consumer.class, ResolvableType.forClassWithGenerics(Flux.class, String.class)).getType()); - catalog.register(functionRegistration); - - FunctionInvocationWrapper lookedUpFunction = catalog - .lookup("reactiveFluxSupplier|reactiveFluxConsumer"); - - assertThat(lookedUpFunction).isNotNull(); - lookedUpFunction.apply(null); - assertThat(consumerDowncounter.get()).isZero(); - } - - @Test - void biConsumerUserFunctionTypeIsKnownInFunctionInvocationWrapper() { - BiConsumer testBiConsumer = (a, b) -> { }; - Function wrappedFunction = x -> { - testBiConsumer.accept(null, null); - return null; - }; - FunctionRegistration> registration = new FunctionRegistration<>( - wrappedFunction, "functionWrappingBiConsumer").type(Function.class); - registration.setUserFunction(testBiConsumer); - SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, - new JacksonMapper(new ObjectMapper())); - catalog.register(registration); - - FunctionInvocationWrapper functionInvocationWrapper = catalog.lookup("functionWrappingBiConsumer"); - - assertThat(functionInvocationWrapper.isWrappedBiConsumer()).isTrue(); - } - - public Function uppercase() { - return v -> v.toUpperCase(); - } - - - public Function hash() { - return v -> v.hashCode(); - } - - public Supplier supplier() { - return () -> 4; - } - - public Consumer consumer() { - return System.out::println; - } - - public Consumer> reactiveConsumer() { - return flux -> flux.subscribe(v -> { - System.out.println(v); - }); - } - - private final AtomicInteger consumerDowncounter = new AtomicInteger(10); - - public Supplier> reactiveFluxSupplier() { - return () -> Flux.fromStream( - IntStream.range(0, consumerDowncounter.get()).boxed().map(i -> Integer.toString(i)) - ); - } - - public Consumer> reactiveFluxConsumer() { - return flux -> flux.subscribe(v -> consumerDowncounter.decrementAndGet()); - } - - private FunctionCatalog configureCatalog(Class... configClass) { - ApplicationContext context = new SpringApplicationBuilder(configClass) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.main.lazy-initialization=true"); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - return catalog; - } - - @EnableAutoConfiguration - private static class CustomConverterConfiguration { - @Bean - public MessageConverter stringToPersonConverter() { - return new AbstractMessageConverter(MimeType.valueOf("text/person")) { - @Override - protected Object convertFromInternal(Message message, Class targetClass, @Nullable Object conversionHint) { - String payload = message.getPayload() instanceof byte[] ? new String((byte[]) message.getPayload()) : (String) message.getPayload(); - Person person = new Person(); - person.setName(payload); - return person; - } - - @Override - protected boolean canConvertFrom(Message message, @Nullable Class targetClass) { - return supportsMimeType(message.getHeaders()) && Person.class.isAssignableFrom(targetClass) && ( - message.getPayload() instanceof String || message.getPayload() instanceof byte[]); - } - - @Override - public Object convertToInternal(Object rawPayload, MessageHeaders headers, Object conversionHint) { - return rawPayload.toString(); - } - - @Override - protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) { - return true; - } - - @Override - protected boolean supports(Class clazz) { - throw new UnsupportedOperationException(); - } - }; - } - - @Bean - public Function func() { - return person -> person.getName(); - } - } - - public static class Person { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - return this.name; - } - } - - private static class Words implements Supplier { - - @Override - public String get() { - return "hello"; - } - - } - - private static class UpperCase implements Function { - - @Override - public String apply(String t) { - return t.toUpperCase(); - } - - } - - private static class Echo implements Function { - - @Override - public Object apply(Object t) { - return t; - } - - } - - private static class UpperCaseMessage - implements Function, Message> { - - @Override - public Message apply(Message t) { - return MessageBuilder.withPayload(t.getPayload().toUpperCase()) - .copyHeaders(t.getHeaders()).build(); - } - - } - - private static class Reverse implements Function { - - @Override - public String apply(String t) { - return new StringBuilder(t).reverse().toString(); - } - - } - - private static class ReverseMessage - implements Function, Message> { - - @Override - public Message apply(Message t) { - return MessageBuilder - .withPayload(new StringBuilder(t.getPayload()).reverse().toString()) - .copyHeaders(t.getHeaders()).build(); - } - - } - - private static class TestFunction implements Function { - - @Override - public String apply(Integer t) { - return "i=" + t; - } - - } - - private static class ReactiveFunction implements Function>>, Flux>> { - - @Override - public Flux> apply(Flux>> listFlux) { - return listFlux - .map(Message::getPayload) - .map(lst -> lst.stream().map(Person::getName).collect(Collectors.toList())); - } - } - - private static class ReactiveMonoGreeter implements Supplier>> { - - @Override - public Mono> get() { - return Mono.just(MessageBuilder.withPayload("hello").build()); - } - - } - - private static class HeaderEnricherFunction implements Function, Message> { - - @Override - public Message apply(Message message) { - return MessageBuilder.withPayload(message.getPayload()).setHeader("original", "newValue") - .build(); - } - } - - private static class StringArrayFunction implements Function { - @Override - public String apply(String[] t) { - return Arrays.asList(t).toString(); - } - } - - private static class StringListFunction implements Function, String> { - @Override - public String apply(List t) { - return t.toString(); - } - } - - private static class TypelessFunction implements Function { - @Override - public String apply(Object t) { - return t.toString(); - } - } - - private static class ByteArrayFunction implements Function { - @Override - public String apply(byte[] t) { - return new String(t); - } - } - -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationConditionalLoadingTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationConditionalLoadingTests.java deleted file mode 100644 index 62b8d06fe..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationConditionalLoadingTests.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import io.cloudevents.spring.messaging.CloudEventMessageConverter; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.test.context.FilteredClassLoader; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.scan.TestFunction; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Tests the conditional loading aspects of the {@link ContextFunctionCatalogAutoConfiguration}. - * - * @author Chris Bono - */ -public class ContextFunctionCatalogAutoConfigurationConditionalLoadingTests { - - protected final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(ContextFunctionCatalogAutoConfiguration.class)); - - @Test - void autoConfigDisabledWhenCustomFunctionCatalogExists() { - contextRunner.withBean(FunctionCatalog.class, () -> mock(FunctionCatalog.class)) - .run((context) -> assertThat(context).doesNotHaveBean(FunctionRegistry.class)); - } - - @Nested - class CloudEventsMessageConverterConfig { - - @Test - void cloudEventsMessageConverterBeanLoadedWhenCloudEventsOnClasspath() { - contextRunner.run((context) -> assertThat(context).hasSingleBean(CloudEventMessageConverter.class)); - } - - @Test - void cloudEventsMessageConverterBeanNotLoadedWhenCloudEventsNotOnClasspath() { - contextRunner.withClassLoader(new FilteredClassLoader(CloudEventMessageConverter.class)).run((context) -> - assertThat(context).doesNotHaveBean(CloudEventMessageConverter.class)); - } - - @Test - void customCloudEventsMessageConverterIsRespected() { - CloudEventMessageConverter customConverter = mock(CloudEventMessageConverter.class); - contextRunner.withBean(CloudEventMessageConverter.class, () -> customConverter) - .run((context) -> assertThat(context).getBean(CloudEventMessageConverter.class).isSameAs(customConverter)); - } - } - - @Nested - class PlainFunctionScanConfig { - - @Test - void functionScanConfigEnabledByDefault() { - contextRunner.withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName()) - .run((context) -> assertThat(context).hasSingleBean(TestFunction.class)); - } - - @Test - void functionScanConfigEnabledWhenEnabledPropertySetToTrue() { - contextRunner.withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName(), - "spring.cloud.function.scan.enabled:true") - .run((context) -> assertThat(context).hasSingleBean(TestFunction.class)); - } - - @Test - void functionScanConfigEnabledWithScanPackagesPointingToNoFunctions() { - contextRunner.withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName() + ".faux", - "spring.cloud.function.scan.enabled:true") - .run((context) -> assertThat(context).doesNotHaveBean(TestFunction.class)); - } - - @Test - void functionScanConfigDisabledWhenEnabledPropertySetToFalse() { - contextRunner.withPropertyValues("spring.cloud.function.scan.packages:" + TestFunction.class.getPackageName(), - "spring.cloud.function.scan.enabled:false") - .run((context) -> assertThat(context).doesNotHaveBean(TestFunction.class)); - } - - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java deleted file mode 100644 index 6e4230b0d..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java +++ /dev/null @@ -1,911 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.lang.reflect.Type; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.config.AbstractFactoryBean; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.inject.FooConfiguration; -import org.springframework.cloud.function.scan.ScannedFunction; -import org.springframework.cloud.function.test.GenericFunction; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.env.Environment; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.DescriptiveResource; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.stereotype.Component; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * @author Artem Bilan - * @author Oleg Zhurakousky - * @author Anshul Mehra - */ -public class ContextFunctionCatalogAutoConfigurationTests { - - private ConfigurableApplicationContext context; - - private FunctionCatalog catalog; - - @AfterEach - public void close() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void lookUps() { - create(SimpleConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")) - .isInstanceOf(Function.class); - assertThat(this.context.getBean("function2")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, - "function,function2")).isInstanceOf(Function.class); - Function, Flux> f = this.catalog.lookup(Function.class, - "function,function2,function3"); - assertThat(f).isInstanceOf(Function.class); - assertThat(f.apply(Flux.just("hello")).blockFirst()) - .isEqualTo("HELLOfunction2function3"); - assertThat(this.context.getBean("supplierFoo")).isInstanceOf(Supplier.class); - assertThat((Supplier) this.catalog.lookup(Supplier.class, "supplierFoo")) - .isInstanceOf(Supplier.class); - assertThat(this.context.getBean("supplier_Foo")).isInstanceOf(Supplier.class); - } - - @Test - // do we really need this test and behavior? What does this even mean? - public void ambiguousFunction() { - create(AmbiguousConfiguration.class); - assertThat(this.context.getBean("foos")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "foos")) - .isInstanceOf(Function.class); - assertThat((Supplier) this.catalog.lookup(Supplier.class, "foos")) - .isInstanceOf(Supplier.class); - Class inputType = ((FunctionInvocationWrapper) this.catalog.lookup(Function.class, "foos")).getRawInputType(); - assertThat(inputType).isEqualTo(String.class); - FunctionInvocationWrapper function = this.catalog.lookup("foos"); - Type outputType = function.getOutputType(); - assertThat((Class) FunctionTypeUtils.getGenericType(outputType)).isEqualTo(Foo.class); - } - - @Test - public void configurationFunction() { - create(FunctionConfiguration.class); - assertThat(this.context.getBean("foos")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup(Function.class, "foos"); - assertThat(function).isInstanceOf(Function.class); - Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getGenericType(inputType)).isEqualTo(String.class); - Type outputType = function.getOutputType(); - assertThat((Class) FunctionTypeUtils.getGenericType(outputType)).isEqualTo(Foo.class); - } - - @Test - public void dependencyInjection() { - create(DependencyInjectionConfiguration.class); - assertThat(this.context.getBean("foos")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "foos")) - .isInstanceOf(Function.class); - Class inputType = ((FunctionInvocationWrapper) this.catalog.lookup(Function.class, "foos")).getRawInputType(); - assertThat(inputType).isEqualTo(String.class); - } - - @Test - public void externalDependencyInjection() { - create(ExternalDependencyConfiguration.class); - assertThat(this.context.getBean("foos")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "foos")) - .isInstanceOf(Function.class); - Class inputType = ((FunctionInvocationWrapper) this.catalog.lookup(Function.class, "foos")).getRawInputType(); - assertThat(inputType).isEqualTo(String.class); - } - - @Test - public void composedFunction() { - create(MultipleConfiguration.class); - FunctionInvocationWrapper function = this.catalog.lookup(Function.class, "foos"); - assertThat(function).isInstanceOf(Function.class); - - function = this.catalog.lookup(Function.class, "foos,bars"); - Class inputType = function.getRawInputType(); - assertThat(inputType).isAssignableFrom(String.class); - Class outputType = function.getRawOutputType(); - assertThat(outputType).isAssignableFrom(Bar.class); - } - - @Test - public void composedSupplier() { - create(MultipleConfiguration.class); - FunctionInvocationWrapper function = this.catalog.lookup("names,foos"); - assertThat(function).isInstanceOf(Supplier.class); - assertThat(function.getRawOutputType()).isAssignableFrom(Foo.class); - assertThat(function.getRawInputType()).isNull(); - } - - @Test - public void composedConsumer() { - create(MultipleConfiguration.class); - FunctionInvocationWrapper function = this.catalog.lookup("foos,print"); - assertThat(function).isInstanceOf(Consumer.class); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.getRawInputType()).isAssignableFrom(String.class); - assertThat(function.getRawOutputType()).isNull(); - } - - @Test - public void genericFunction() { - create(GenericConfiguration.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.getRawInputType()).isAssignableFrom(Map.class); - } - - @Test - public void fluxMessageFunction() { - create(FluxMessageConfiguration.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.isInputTypeMessage()).isTrue(); - - final Type inputType = function.getInputType(); - - assertThat(FunctionTypeUtils.getRawType(inputType)).isAssignableFrom(Flux.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Message.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(FunctionTypeUtils.getGenericType(inputType)))).isAssignableFrom(String.class); - } - - @Test - public void publisherMessageFunction() { - create(PublisherMessageConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.isInputTypeMessage()).isTrue(); - - final Type inputType = function.getInputType(); - - assertThat(FunctionTypeUtils.getRawType(inputType)).isAssignableFrom(Publisher.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Message.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(FunctionTypeUtils.getGenericType(inputType)))).isAssignableFrom(String.class); - - } - - @Test - public void monoFunction() { - create(MonoConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.isInputTypeMessage()).isFalse(); - Type inputType = function.getInputType(); - Type outputType = function.getOutputType(); - assertThat((Class) FunctionTypeUtils.getGenericType(inputType)).isAssignableFrom(String.class); - assertThat(FunctionTypeUtils.getRawType(inputType)).isAssignableFrom(Flux.class); - assertThat(FunctionTypeUtils.getRawType(outputType)).isAssignableFrom(Mono.class); - } - - @Test - public void monoToMonoNonVoidFunction() { - create(MonoToMonoNonVoidConfiguration.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - Type inputType = function.getInputType(); - assertThat((Class) FunctionTypeUtils.getGenericType(inputType)).isAssignableFrom(String.class); - Type outputType = function.getOutputType(); - assertThat((Class) FunctionTypeUtils.getGenericType(outputType)).isAssignableFrom(String.class); - } - - @Test - public void messageFunction() { - create(MessageConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.isInputTypeMessage()).isTrue(); - assertThat(function.isOutputTypeMessage()).isTrue(); - Type inputType = function.getInputType(); - assertThat((Class) FunctionTypeUtils.getGenericType(inputType)).isAssignableFrom(String.class); - } - - @Test - public void genericFluxFunction() { - create(GenericFluxConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Map.class); - assertThat(FunctionTypeUtils.getRawType(inputType)).isAssignableFrom(Flux.class); - } - - @Test - public void externalFunction() { - create(ExternalConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Map.class); - } - - @Test - public void singletonFunction() { - create(SingletonConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.isInputTypePublisher()).isFalse(); - assertThat(function.isOutputTypePublisher()).isFalse(); - Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Integer.class); - } - - @Test - @Disabled - public void singletonMessageFunction() { - create(SingletonMessageConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.isInputTypeMessage()).isTrue(); - Type inputType = function.getInputType(); - assertThat((Class) FunctionTypeUtils.getGenericType(inputType)).isAssignableFrom(Integer.class); - } - - @Test - public void nonParametericTypeFunction() { - create(NonParametricTypeSingletonConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - Type inputType = function.getInputType(); - assertThat((Class) FunctionTypeUtils.getGenericType(inputType)).isAssignableFrom(Integer.class); - } - - @Test - public void componentScanBeanFunction() { - create(ComponentScanBeanConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Map.class); - } - - @Test - public void componentScanFunction() { - create(ComponentScanConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("function"); - assertThat(function).isInstanceOf(Function.class); - Type inputType = function.getInputType(); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(inputType))).isAssignableFrom(Map.class); - } - - @Test - public void componentScanJarFunction() { - try { - create("greeter.jar", ComponentScanJarConfiguration.class); - assertThat(this.context.getBean("greeter")).isInstanceOf(Function.class); - FunctionInvocationWrapper function = this.catalog.lookup("greeter"); - assertThat(function).isInstanceOf(Function.class); - Type inputType = function.getInputType(); - assertThat((Class) FunctionTypeUtils.getGenericType(inputType)).isAssignableFrom(String.class); - } - finally { - ClassUtils.overrideThreadContextClassLoader(getClass().getClassLoader()); - } - } - - private void create(String jarfile, Class config, String... props) { - try { - URL[] urls = new URL[] { new ClassPathResource(jarfile).getURL() }; - ClassUtils.overrideThreadContextClassLoader( - new URLClassLoader(urls, getClass().getClassLoader())); - create(config, props); - } - catch (Exception e) { - ReflectionUtils.rethrowRuntimeException(e); - } - } - - @Test - public void simpleFunction() { - create(SimpleConfiguration.class); - Object bean = this.context.getBean("function"); - assertThat(bean).isInstanceOf(Function.class); - Function, Flux> function = this.catalog - .lookup(Function.class, "function"); - assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); - assertThat(bean).isNotSameAs(function); - } - - @Test - @Disabled - public void simpleSupplier() { - create(SimpleConfiguration.class); - assertThat(this.context.getBean("supplier")).isInstanceOf(Supplier.class); - Supplier> supplier = this.catalog.lookup(Supplier.class, "supplier"); - assertThat(supplier.get().blockFirst()).isEqualTo("hello"); - } - - @Test - public void simpleConsumer() { - create(SimpleConfiguration.class); - assertThat(this.context.getBean("consumer")).isInstanceOf(Consumer.class); - Function, Mono> consumer = this.catalog.lookup(Function.class, - "consumer"); - consumer.apply(Flux.just("foo", "bar")).subscribe(); - assertThat(this.context.getBean(SimpleConfiguration.class).list).hasSize(2); - } - - @Test - @Disabled - public void qualifiedBean() { - create(QualifiedConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup("function")).isNull(); - assertThat((Function) this.catalog.lookup("other")).isNotNull(); - assertThat(FunctionTypeUtils.getGenericType(((FunctionInvocationWrapper) this.catalog.lookup("other")).getInputType())) - .isEqualTo(String.class); - } - - @Test - public void aliasBean() { - create(AliasConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")) - .isNotNull(); - assertThat((Function) this.catalog.lookup(Function.class, "other")) - .isInstanceOf(Function.class); - } - - @Test - @Disabled - public void registrationBean() { - create(RegistrationConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "registration")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "other")) - .isInstanceOf(Function.class); - } - - @Test - public void factoryBeanFunction() { - create(FactoryBeanConfiguration.class); - assertThat(this.context.getBean("function")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")) - .isInstanceOf(Function.class); - Function, Flux> f = this.catalog.lookup(Function.class, - "function"); - assertThat(f.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO-bar"); - } - - @Test - public void functionCatalogDependentBeanFactoryPostProcessor() { - create(new Class[]{ComponentFunctionConfiguration.class, AppendFunction.class}); - assertThat(this.context.getBean("appendFunction")).isInstanceOf(Function.class); - assertThat((Function) this.catalog.lookup(Function.class, "appendFunction")) - .isInstanceOf(Function.class); - Function, Flux> f = this.catalog.lookup(Function.class, - "appendFunction"); - assertThat(f.apply(Flux.just("World")).blockFirst()).isEqualTo("Hello World"); - } - - private void create(Class type, String... props) { - create(new Class[] { type }, props); - } - - private void create(Class[] types, String... props) { - this.context = new SpringApplicationBuilder(types).properties(props).run(); - this.catalog = this.context.getBean(FunctionCatalog.class); -// this.inspector = this.context.getBean(FunctionInspector.class); - } - - @EnableAutoConfiguration - @Configuration - protected static class EmptyConfiguration { - - } - - @EnableAutoConfiguration - @Configuration - protected static class SimpleConfiguration { - - private List list = new ArrayList<>(); - - @Bean - public Function function() { - return value -> value.toUpperCase(); - } - - @Bean - public Function function2() { - return value -> value + "function2"; - } - - @Bean - public Function function3() { - return value -> value + "function3"; - } - - @Bean - public Supplier supplier() { - return () -> "hello"; - } - - @Bean(name = { "supplierFoo", "supplier_Foo" }) - public Supplier foo() { - return () -> "hello"; - } - - @Bean - public Consumer consumer() { - return value -> { - this.list.add(value); - }; - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class ComponentFunctionConfiguration { - @Bean - public String value() { - return "Hello "; - } - - @Bean - public BeanFactoryPostProcessor someBeanFactoryPostProcessor(Environment environment, - @Nullable FunctionRegistry functionCatalog) { - return beanFactory -> { }; - } - } - - @Component("appendFunction") - public static class AppendFunction implements Function { - private String value; - - public AppendFunction(String value) { - this.value = value; - } - - @Override - public String apply(String s) { - return this.value + s; - } - } - - @EnableAutoConfiguration - @Configuration - protected static class DependencyInjectionConfiguration { - - @Bean - public Function foos(String foo) { - return value -> new Foo(foo + ": " + value.toUpperCase()); - } - - @Bean - public String value() { - return "Hello"; - } - - } - - @EnableAutoConfiguration - @Configuration("foos") - protected static class FunctionConfiguration - implements Function, Flux> { - - @Override - public Flux apply(Flux flux) { - return flux.map(foo -> new Foo(value() + ": " + foo.toUpperCase())); - } - - @Bean - public String value() { - return "Hello"; - } - - } - - @EnableAutoConfiguration - @Configuration - @ComponentScan(basePackageClasses = FooConfiguration.class) - protected static class ExternalDependencyConfiguration { - - @Bean - public String value() { - return "Hello"; - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class AmbiguousConfiguration { - - @Bean - public Function foos() { - return value -> new Foo(value.toUpperCase()); - } - - @Bean - @Qualifier("foos") - public Supplier supplier() { - return () -> new Foo("bar"); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class MultipleConfiguration { - - @Bean - public Function foos() { - return value -> new Foo(value.toUpperCase()); - } - - @Bean - public Function bars() { - return value -> new Bar(value.getValue()); - } - - @Bean - public Consumer print() { - return System.out::println; - } - - @Bean - public Supplier names() { - return () -> "Mark"; - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class GenericConfiguration { - - @Bean - public Function, Map> function() { - return m -> m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), - e -> e.getValue().toString().toUpperCase())); - } - - } - - @EnableAutoConfiguration - @Configuration - @Import(GenericFunction.class) - protected static class ExternalConfiguration { - - } - - @EnableAutoConfiguration - @Configuration - protected static class SingletonConfiguration implements BeanFactoryPostProcessor { - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { - beanFactory.registerSingleton("function", new SingletonFunction()); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class SingletonMessageConfiguration - implements BeanFactoryPostProcessor { - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { - beanFactory.registerSingleton("function", new SingletonMessageFunction()); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class NonParametricTypeSingletonConfiguration { - - @Bean - public SingletonFunction function() { - return new SingletonFunction(); - } - - } - - protected static class SingletonFunction implements Function { - - @Override - public String apply(Integer input) { - return "value=" + input; - } - - } - - protected static class SingletonMessageFunction - implements Function, Message> { - - @Override - public Message apply(Message input) { - return MessageBuilder.withPayload("value=" + input.getPayload()).build(); - } - - } - - @EnableAutoConfiguration - @Configuration - @ComponentScan(basePackageClasses = GenericFunction.class) - protected static class ComponentScanBeanConfiguration { - - } - - @EnableAutoConfiguration - @Configuration - @ComponentScan(basePackageClasses = ScannedFunction.class) - protected static class ComponentScanConfiguration { - - } - - @EnableAutoConfiguration - @Configuration - protected static class ComponentScanJarConfiguration { - - } - - @EnableAutoConfiguration - @Configuration - protected static class GenericFluxConfiguration { - - @Bean - public Function>, Flux>> function() { - return flux -> flux.map(m -> m.entrySet().stream().collect(Collectors - .toMap(e -> e.getKey(), e -> e.getValue().toString().toUpperCase()))); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class FluxMessageConfiguration { - - @Bean - public Function>, Flux>> function() { - return flux -> flux.map(m -> MessageBuilder - .withPayload(m.getPayload().toUpperCase()).build()); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class PublisherMessageConfiguration { - - @Bean - public Function>, Publisher>> function() { - return flux -> Flux.from(flux).map(m -> MessageBuilder - .withPayload(m.getPayload().toUpperCase()).build()); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class MonoConfiguration { - - @Bean - public Function, Mono>> function() { - return flux -> flux.collect(HashMap::new, - (map, word) -> map.merge(word, 1, Integer::sum)); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class MonoToMonoNonVoidConfiguration { - - @Bean - public Function, Mono> function() { - return mono -> mono; - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class MessageConfiguration { - - @Bean - public Function, Message> function() { - return m -> MessageBuilder.withPayload(m.getPayload().toUpperCase()).build(); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class QualifiedConfiguration { - - @Bean - @Qualifier("other") - public Function function() { - return value -> value.toUpperCase(); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class AliasConfiguration { - - @Bean({ "function", "other" }) - public Function function() { - return value -> value.toUpperCase(); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class RegistrationConfiguration { - - @Bean - public FunctionRegistration> registration() { - return new FunctionRegistration>(function(), - "other"); - } - - @Bean - public Function function() { - return value -> value.toUpperCase(); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class FactoryBeanConfiguration - implements BeanDefinitionRegistryPostProcessor { - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) - throws BeansException { - RootBeanDefinition beanDefinition = new RootBeanDefinition( - FunctionFactoryBean.class); - beanDefinition.setSource(new DescriptiveResource("Function")); - registry.registerBeanDefinition("function", beanDefinition); - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { - - } - - } - - private static class FunctionFactoryBean - extends AbstractFactoryBean> { - - @Override - public Class getObjectType() { - return Function.class; - } - - @Override - protected Function createInstance() throws Exception { - return s -> s.toUpperCase() + "-bar"; - } - - } - - public static class Foo { - - private String value; - - public Foo(String value) { - this.value = value; - } - - Foo() { - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - - public static class Bar { - - private String message; - - public Bar(String value) { - this.message = value; - } - - Bar() { - } - - public String getMessage() { - return this.message; - } - - public void setMessage(String message) { - this.message = message; - } - - } - -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java deleted file mode 100644 index 3b3075a73..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.google.gson.Gson; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.scan.TestFunction; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.env.MapPropertySource; -import org.springframework.util.StringUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public class ContextFunctionCatalogInitializerTests { - - private GenericApplicationContext context; - - private FunctionCatalog catalog; - - @AfterEach - public void close() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void lookUps() { - create(SimpleConfiguration.class); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")) - .isInstanceOf(Function.class); - } - - @Test - public void properties() { - create(PropertiesConfiguration.class, "app.greeting=hello"); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); - @SuppressWarnings("unchecked") - Function, Flux> function = (Function, Flux>) this.catalog - .lookup(Function.class, "function"); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("hello foo"); - } - - @Test - public void value() { - create(ValueConfiguration.class, "app.greeting=hello"); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); - @SuppressWarnings("unchecked") - Function, Flux> function = (Function, Flux>) this.catalog - .lookup(Function.class, "function"); - assertThat(function).isInstanceOf(Function.class); - assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("hello foo"); - } - - @Test - @Disabled - public void compose() { - create(SimpleConfiguration.class); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); - @SuppressWarnings("unchecked") - Supplier> supplier = (Supplier>) this.catalog - .lookup(Supplier.class, "supplier|function"); - assertThat(supplier).isInstanceOf(Supplier.class); - assertThat(supplier.get().blockFirst()).isEqualTo("HELLO"); - // TODO: support for function composition - } - - @Test - public void missingType() { - Assertions.assertThrows(BeanCreationException.class, () -> { - create(MissingTypeConfiguration.class); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")) - .isInstanceOf(Function.class); - // TODO: support for type inference from functional bean registrations - }); - } - - @Test - public void dependencyInjection() { - create(DependencyInjectionConfiguration.class); - assertThat(this.context.getBean("foos")).isInstanceOf(FunctionRegistration.class); - assertThat((Function) this.catalog.lookup(Function.class, "foos")) - .isInstanceOf(Function.class); - } - - @Test - public void simpleFunction() { - create(SimpleConfiguration.class); - Object bean = this.context.getBean("function"); - assertThat(bean).isInstanceOf(FunctionRegistration.class); - Function, Flux> function - = this.catalog.lookup(Function.class, "function"); - assertThat(function.apply(Flux.just("{\"name\":\"foo\"}")).blockFirst().getName()).isEqualTo("FOO"); - assertThat(bean).isNotSameAs(function); - } - - @Test - public void scanFunction() { - create(EmptyConfiguration.class, - "spring.cloud.function.scan.packages=org.springframework.cloud.function.context.scan"); - Object bean = this.context.getBean(TestFunction.class.getName()); - assertThat(bean).isInstanceOf(Function.class); - Function, Flux> function = this.catalog - .lookup(Function.class, TestFunction.class.getName()); - assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); - assertThat(bean).isNotSameAs(function); - } - - @Test - public void simpleSupplier() { - create(SimpleConfiguration.class); - assertThat(this.context.getBean("supplier")) - .isInstanceOf(FunctionRegistration.class); - Supplier supplier = this.catalog.lookup(Supplier.class, "supplier"); - assertThat(supplier.get()).isEqualTo("hello"); - } - - @Test - public void simpleConsumer() { - create(SimpleConfiguration.class); - assertThat(this.context.getBean("consumer")) - .isInstanceOf(FunctionRegistration.class); - Function, Mono> consumer = this.catalog.lookup(Function.class, - "consumer"); - consumer.apply(Flux.just("foo", "bar")).subscribe(); - assertThat(this.context.getBean(SimpleConfiguration.class).list).hasSize(2); - } - - @Test - public void overrideGson() { - create(GsonConfiguration.class); - Gson user = this.context.getBean(GsonConfiguration.class).gson(); - Gson bean = this.context.getBean(Gson.class); - assertThat(user).isSameAs(bean); - } - - @SuppressWarnings("unchecked") - private void create( - Class> type, - String... props) { - create(Arrays.asList(BeanUtils.instantiateClass(type)) - .toArray(new ApplicationContextInitializer[0]), props); - } - - private void create(ApplicationContextInitializer[] types, - String... props) { - this.context = new GenericApplicationContext(); - Map map = new HashMap<>(); - for (String prop : props) { - String[] array = StringUtils.delimitedListToStringArray(prop, "="); - String key = array[0]; - String value = array.length > 1 ? array[1] : ""; - map.put(key, value); - } - if (!map.isEmpty()) { - this.context.getEnvironment().getPropertySources() - .addFirst(new MapPropertySource("testProperties", map)); - } - for (ApplicationContextInitializer type : types) { - type.initialize(this.context); - } - new ContextFunctionCatalogInitializer.ContextFunctionCatalogBeanRegistrar( - this.context).postProcessBeanDefinitionRegistry(this.context); - this.context.refresh(); - this.catalog = this.context.getBean(FunctionCatalog.class); - } - - protected static class EmptyConfiguration - implements ApplicationContextInitializer { - - @Override - public void initialize(GenericApplicationContext applicationContext) { - } - - } - - protected static class MissingTypeConfiguration - implements ApplicationContextInitializer { - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function())); - } - - @Bean - public Function function() { - return value -> value.toUpperCase(); - } - - } - - protected static class SimpleConfiguration - implements ApplicationContextInitializer { - - private List list = new ArrayList<>(); - - - @Override - public void initialize(GenericApplicationContext context) { - - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function()).type(FunctionTypeUtils.functionType(Person.class, Person.class))); - context.registerBean("supplier", FunctionRegistration.class, - () -> new FunctionRegistration<>(supplier()) - .type(FunctionTypeUtils.supplierType(String.class))); - context.registerBean("consumer", FunctionRegistration.class, - () -> new FunctionRegistration<>(consumer()) - .type(FunctionTypeUtils.consumerType(String.class))); - context.registerBean(SimpleConfiguration.class, () -> this); - } - - @Bean - public Function function() { - return person -> { - Person p = new Person(); - p.setName(person.getName().toUpperCase()); - return p; - }; - } - - @Bean - public Supplier supplier() { - return () -> "hello"; - } - - @Bean - public Consumer consumer() { - return value -> this.list.add(value); - } - } - - @ConfigurationProperties("app") - protected static class PropertiesConfiguration - implements ApplicationContextInitializer { - - private String greeting; - - public String getGreeting() { - return this.greeting; - } - - public void setGreeting(String greeting) { - this.greeting = greeting; - } - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function()).type(FunctionTypeUtils.functionType(String.class, String.class))); - context.registerBean(PropertiesConfiguration.class, () -> this); - } - - @Bean - public Function function() { - return value -> this.greeting + " " + value; - } - - } - - protected static class ValueConfiguration - implements ApplicationContextInitializer { - - @Value("${app.greeting}") - private String greeting; - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function()).type(FunctionTypeUtils.functionType(String.class, String.class))); - context.registerBean(ValueConfiguration.class, () -> this); - } - - @Bean - public Function function() { - return value -> this.greeting + " " + value; - } - - } - - protected static class GsonConfiguration - implements ApplicationContextInitializer { - - private Gson gson = new Gson(); - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("gson", Gson.class, this::gson); - context.registerBean(GsonConfiguration.class, () -> this); - } - - @Bean - public Gson gson() { - return this.gson; - } - - } - - protected static class DependencyInjectionConfiguration - implements ApplicationContextInitializer { - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean(String.class, () -> value()); - context.registerBean("foos", FunctionRegistration.class, - () -> new FunctionRegistration<>(foos(context.getBean(String.class))) - .type(FunctionTypeUtils.functionType(String.class, Foo.class))); - } - - @Bean - public Function foos(String foo) { - return value -> new Foo(foo + ": " + value.toUpperCase()); - } - - @Bean - public String value() { - return "Hello"; - } - - } - - protected static class FunctionConfiguration - implements Function, Flux>, - ApplicationContextInitializer { - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("foos", FunctionConfiguration.class, () -> this); - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(this, "foos") - .type(FunctionConfiguration.class)); - } - - @Override - public Flux apply(Flux flux) { - return flux.map(foo -> new Foo(value() + ": " + foo.toUpperCase())); - } - - @Bean - public String value() { - return "Hello"; - } - - } - - public static class Foo { - - private String value; - - public Foo(String value) { - this.value = value; - } - - Foo() { - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - - private static class Person { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/JsonMessageConverterTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/JsonMessageConverterTests.java deleted file mode 100644 index 5af30522e..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/JsonMessageConverterTests.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Test; - -import org.springframework.cloud.function.json.JacksonMapper; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeTypeUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class JsonMessageConverterTests { - - @Test - public void testTypeInference() { - JsonMessageConverter converter = new JsonMessageConverter(new JacksonMapper(new ObjectMapper())); - - Message message = MessageBuilder.withPayload("{\"name\":\"bill\"}").build(); - assertThat(converter.canConvertFrom(message, Person.class)).isTrue(); - - message = MessageBuilder.withPayload("{\"name\":\"bill\"}").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build(); - assertThat(converter.canConvertFrom(message, Person.class)).isTrue(); - assertThat(converter.canConvertFrom(message, Object.class)).isFalse(); - assertThat(converter.canConvertFrom(message, null)).isFalse(); - assertThat(converter.convertFromInternal(message, Person.class, null)).isInstanceOf(Person.class); - - message = MessageBuilder.withPayload("{\"name\":\"bill\"}") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON.toString() + ";type=" + Person.class.getName()).build(); - assertThat(converter.canConvertFrom(message, Object.class)).isTrue(); - assertThat(converter.canConvertFrom(message, null)).isTrue(); - assertThat(converter.convertFromInternal(message, Person.class, null)).isInstanceOf(Person.class); - assertThat(converter.convertFromInternal(message, Object.class, null)).isInstanceOf(Person.class); - assertThat(converter.convertFromInternal(message, null, null)).isInstanceOf(Person.class); - } - - public static class Person { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java deleted file mode 100644 index 591d60b09..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.config; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.DefaultMessageRoutingHandler; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.MessageRoutingCallback; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class RoutingFunctionTests { - - private ConfigurableApplicationContext context; - - @AfterEach - public void before() { - System.clearProperty("spring.cloud.function.definition"); - System.clearProperty("spring.cloud.function.routing-expression"); - context.close(); - } - - private FunctionCatalog configureCatalog(Class configurationClass) { - context = new SpringApplicationBuilder(configurationClass).run( - "--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.routing.enabled=true"); - return context.getBean(FunctionCatalog.class); - } - - private FunctionCatalog configureCatalog() { - return configureCatalog(RoutingFunctionConfiguration.class); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testDefaultRouting() { - Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "blah").build(); - - FunctionCatalog functionCatalog = this.configureCatalog(EmptyConfiguration.class); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - try { - function.apply(message); - fail(); - } - catch (Exception e) { - // Good - } - // - functionCatalog = this.configureCatalog(ConfigurationWithDefaultMessageRoutingHandler.class); - function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - function.apply(message); - ConfigurationWithDefaultMessageRoutingHandler config = this.context.getBean(ConfigurationWithDefaultMessageRoutingHandler.class); - assertThat(config.defaultHandlerInvoked).isTrue(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testInvocationWithMessageAndHeader() { - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "reverse").build(); - assertThat(function.apply(message)).isEqualTo("olleh"); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testRoutingSimpleInputWithReactiveFunctionWithMessageHeader() { - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "echoFlux").build(); - assertThat(((Flux) function.apply(message)).blockFirst()).isEqualTo("hello"); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testRoutingReactiveInputWithReactiveFunctionAndDefinitionMessageHeader() { - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "echoFlux").build(); - Flux resultFlux = (Flux) function.apply(Flux.just(message)); - - StepVerifier.create(resultFlux).expectError().verify(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testRoutingReactiveInputWithReactiveFunctionAndExpressionMessageHeader() { - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".routing-expression", "'echoFlux'").build(); - Flux resultFlux = (Flux) function.apply(Flux.just(message)); - StepVerifier.create(resultFlux).expectError().verify(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void failWithHeaderProvidedExpressionAccessingRuntime() { - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".routing-expression", - "T(java.lang.Runtime).getRuntime().exec(\"open -a calculator.app\")") - .build(); - try { - function.apply(message); - fail(); - } - catch (Exception e) { - assertThat(e.getMessage()).isEqualTo("EL1005E: Type cannot be found 'java.lang.Runtime'"); - } - - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testInvocationWithMessageAndDefinitionProperty() { - System.setProperty(FunctionProperties.PREFIX + ".definition", "reverse"); - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello").build(); - assertThat(function.apply(message)).isEqualTo("olleh"); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testInvocationWithMessageAndRoutingExpression() { - System.setProperty(FunctionProperties.PREFIX + ".routing-expression", "headers.function_name"); - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello").setHeader("function_name", "reverse").build(); - assertThat(function.apply(message)).isEqualTo("olleh"); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void testInvocationWithMessageAndRoutingExpressionCaseInsensitive() { - System.setProperty(FunctionProperties.PREFIX + ".routing-expression", "headers.function_Name"); - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello").setHeader("function_name", "reverse").build(); - assertThat(function.apply(message)).isEqualTo("olleh"); - - System.setProperty(FunctionProperties.PREFIX + ".routing-expression", "headers.FunCtion_namE"); - assertThat(function.apply(message)).isEqualTo("olleh"); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testInvocationWithRoutingBeanExpression() { - System.setProperty(FunctionProperties.PREFIX + ".routing-expression", - "@reverse.apply(#root.getHeaders().get('func'))"); - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello").setHeader("func", "esacreppu").build(); - assertThat(function.apply(message)).isEqualTo("HELLO"); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testOtherExpectedFailures() { - FunctionCatalog functionCatalog = this.configureCatalog(); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - // no function.definition header or function property - try { - function.apply(MessageBuilder.withPayload("hello").build()); - Assertions.fail(); - } - catch (Exception e) { - // ignore - } - - // non existing function - try { - function.apply(MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "blah").build()); - Assertions.fail(); - } - catch (Exception e) { - // ignore - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testInvocationWithMessageComposed() { - FunctionCatalog functionCatalog = this.configureCatalog(); - - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME + "|reverse"); - assertThat(function).isNotNull(); - - Message message = MessageBuilder.withPayload("hello") - .setHeader(FunctionProperties.PREFIX + ".definition", "uppercase").build(); - - assertThat(function.apply(message)).isEqualTo("OLLEH"); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testMultipleRouters() { - System.setProperty(FunctionProperties.PREFIX + ".routing-expression", "'uppercase'"); - FunctionCatalog functionCatalog = this.configureCatalog(MultipleRouterConfiguration.class); - Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME); - assertThat(function).isNotNull(); - Message message = MessageBuilder.withPayload("hello").build(); - assertThat(function.apply(message)).isEqualTo("HELLO"); - - function = functionCatalog.lookup("mySpecialRouter"); - assertThat(function).isNotNull(); - message = MessageBuilder.withPayload("hello").build(); - assertThat(function.apply(message)).isEqualTo("olleh"); - } - - @EnableAutoConfiguration - @Configuration - protected static class RoutingFunctionConfiguration { - - @Bean - public Function reverse() { - return v -> new StringBuilder(v).reverse().toString(); - } - - @Bean - public Function uppercase() { - return String::toUpperCase; - } - - @Bean - public Function, Flux> echoFlux() { - return f -> f; - } - } - - @EnableAutoConfiguration - @Configuration - protected static class MultipleRouterConfiguration { - - @Bean - RoutingFunction mySpecialRouter(FunctionCatalog functionCatalog, BeanFactory beanFactory, @Nullable MessageRoutingCallback routingCallback) { - Map propertiesMap = new HashMap<>(); - propertiesMap.put(FunctionProperties.PREFIX + ".routing-expression", "'reverse'"); - return new RoutingFunction(functionCatalog, propertiesMap, new BeanFactoryResolver(beanFactory), routingCallback); - } - - @Bean - public Function reverse() { - return v -> new StringBuilder(v).reverse().toString(); - } - - @Bean - public Function uppercase() { - return String::toUpperCase; - } - } - - @EnableAutoConfiguration - @Configuration - protected static class EmptyConfiguration { - } - - @EnableAutoConfiguration - @Configuration - protected static class ConfigurationWithDefaultMessageRoutingHandler { - public boolean defaultHandlerInvoked; - @Bean - public DefaultMessageRoutingHandler defaultRoutingHandler() { - return new DefaultMessageRoutingHandler() { - @Override - public void accept(Message message) { - super.accept(message); - defaultHandlerInvoked = true; - } - }; - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java deleted file mode 100644 index ad12ee4b9..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.scan; - -import java.util.function.Function; - -/** - * @author Dave Syer - * - */ -public class TestFunction implements Function { - - @Override - public String apply(String t) { - return t.toUpperCase(); - } - -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/string/FunctionalStringSourceTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/string/FunctionalStringSourceTests.java deleted file mode 100644 index 585e2043a..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/string/FunctionalStringSourceTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.string; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Test that spring.main.sources works with the functional approach. - * - * @author Dave Syer - * - */ -// @checkstyle:off -@FunctionalSpringBootTest(classes = Object.class, properties = "spring.main.sources=org.springframework.cloud.function.context.string.FunctionalStringSourceTests.TestConfiguration") -// @checkstyle:on -public class FunctionalStringSourceTests { - - @Autowired - private FunctionCatalog catalog; - - @Test - public void words() throws Exception { - Function, Flux> function = this.catalog - .lookup(Function.class, "function"); - assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); - } - - protected static class TestConfiguration implements Function { - - @Override - public String apply(String value) { - return value.toUpperCase(); - } - - } - -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/test/FunctionalTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/test/FunctionalTests.java deleted file mode 100644 index a22958732..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/test/FunctionalTests.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context.test; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.cloud.function.context.FunctionCatalog; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@FunctionalSpringBootTest -public class FunctionalTests { - - @Autowired - private FunctionCatalog catalog; - - @Test - public void words() throws Exception { - Function, Flux> function = this.catalog - .lookup(Function.class, "function"); - assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); - } - - @SpringBootConfiguration - protected static class TestConfiguration implements Function { - - @Override - public String apply(String value) { - return value.toUpperCase(); - } - - } - -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/inject/FooConfiguration.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/inject/FooConfiguration.java deleted file mode 100644 index e7db39122..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/inject/FooConfiguration.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.inject; - -import java.util.function.Function; - -import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfigurationTests.Foo; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class FooConfiguration { - - @Bean - public Function foos(String foo) { - return value -> new Foo(foo + ": " + value.toUpperCase()); - } - -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java deleted file mode 100644 index 83f49d38d..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.scan; - -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.springframework.stereotype.Component; - -/** - * @author Dave Syer - * - */ -@Component("function") -public class ScannedFunction - implements Function, Map> { - - @Override - public Map apply(Map m) { - return m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), - e -> e.getValue().toString().toUpperCase())); - } - -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/test/GenericFunction.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/test/GenericFunction.java deleted file mode 100644 index 46ae2b9d4..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/test/GenericFunction.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @author Dave Syer - * - */ -@Configuration -public class GenericFunction { - - @Bean - public Function, Map> function() { - return m -> m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), - e -> e.getValue().toString().toUpperCase())); - } - -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/userissues/UserIssuesTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/userissues/UserIssuesTests.java deleted file mode 100644 index 7a2b7c533..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/userissues/UserIssuesTests.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.userissues; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.ResolvableType; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.GenericMessage; - - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class UserIssuesTests { - - private FunctionCatalog configureCatalog(Class... configClass) { - ApplicationContext context = new SpringApplicationBuilder(configClass).run( - "--logging.level.org.springframework.cloud.function=DEBUG", "--spring.main.lazy-initialization=true"); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - return catalog; - } - - @BeforeEach - public void before() { - System.clearProperty("spring.cloud.function.definition"); - } - - @Test - public void testIssue602() throws Exception { - FunctionCatalog catalog = this.configureCatalog(Issue602Configuration.class); - Function, Integer> function = catalog.lookup("consumer"); - int result = function.apply( - new GenericMessage("[{\"name\":\"julien\"},{\"name\":\"ricky\"},{\"name\":\"bubbles\"}]")); - assertThat(result).isEqualTo(3); - } - - @Test - public void testIssue602asPOJO() throws Exception { - FunctionCatalog catalog = this.configureCatalog(Issue602Configuration.class); - Function>, Integer> function = catalog.lookup("consumer"); - ArrayList products = new ArrayList<>(); - Product p = new Product(); - p.setName("julien"); - products.add(p); - p = new Product(); - p.setName("ricky"); - products.add(p); - p = new Product(); - p.setName("bubbles"); - products.add(p); - int result = function.apply(new GenericMessage>(products)); - assertThat(result).isEqualTo(3); - - } - - @Test - public void testIssue602asCollectionOfUnconvertedItems() throws Exception { - FunctionCatalog catalog = this.configureCatalog(Issue602Configuration.class); - Function>, Integer> function = catalog.lookup("consumer"); - ArrayList products = new ArrayList<>(); - products.add("{\"name\":\"julien\"}"); - products.add("{\"name\":\"ricky\"}"); - products.add("{\"name\":\"bubbles\"}"); - int result = function.apply(new GenericMessage>(products)); - assertThat(result).isEqualTo(3); - - } - - @SuppressWarnings("unchecked") - @Test - public void testIssue601() throws Exception { - FunctionCatalog catalog = this.configureCatalog(Issue601Configuration.class); - FunctionInvocationWrapper function = catalog.lookup("uppercase"); - assertThat(function.getInputType().getTypeName()) - .isEqualTo(ResolvableType.forClassWithGenerics(Flux.class, String.class).getType().getTypeName()); - assertThat(function.getOutputType().getTypeName()) - .isEqualTo(ResolvableType.forClassWithGenerics(Flux.class, Integer.class).getType().getTypeName()); - Flux result = (Flux) function.apply(Flux.just("julien", "ricky", "bubbles")); - List results = result.collectList().block(); - assertThat(results.get(0)).isEqualTo(6); - assertThat(results.get(1)).isEqualTo(5); - assertThat(results.get(2)).isEqualTo(7); - } - - @EnableAutoConfiguration - @Configuration - public static class Issue602Configuration { - @Bean - public Function, Integer> consumer() { - return v -> { - assertThat(v.get(0).getName()).isEqualTo("julien"); - assertThat(v.get(1).getName()).isEqualTo("ricky"); - assertThat(v.get(2).getName()).isEqualTo("bubbles"); - return v.size(); - }; - } - } - - @EnableAutoConfiguration - @Configuration - public static class Issue601Configuration { - @Bean - public Uppercase uppercase() { - return new Uppercase(); - } - } - - public static class Uppercase implements Function, Flux> { - - @Override - public Flux apply(Flux s) { - return s.map(v -> v.length()); - } - } - - public static class Product { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMapperTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMapperTests.java deleted file mode 100644 index 30207d367..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMapperTests.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.utils; - -import java.util.List; -import java.util.stream.Stream; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.gson.Gson; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import org.springframework.cloud.function.json.GsonMapper; -import org.springframework.cloud.function.json.JacksonMapper; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.core.ResolvableType; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - * - */ -public class JsonMapperTests { - - public static Stream params() { - return Stream.of(new GsonMapper(new Gson()), new JacksonMapper(new ObjectMapper())); - } - - @Test - public void objectNode_isJsonStringRepresentsCollection() { - ObjectNode node = JsonNodeFactory.instance.objectNode(); - node.put("id", "1234ab"); - node.put("foo", "bar"); - - /* - * Passing the ObjectNode directly results in a positive identification as - * a collection, as its distant parent JsonNode implements Iterable. - */ - assertThat(JsonMapper.isJsonStringRepresentsCollection(node)).isFalse(); - - String nodeAsString = node.toString(); - - /* - * Sending the node as a string returns false, however, as the line - * isJsonString(value) && str.startsWith("[") && str.endsWith("]") - * will not be true. - */ - assertThat(JsonMapper.isJsonStringRepresentsCollection(nodeAsString)).isFalse(); - } - - @ParameterizedTest - @MethodSource("params") - public void vanillaArray(JsonMapper mapper) { - String json = "[{\"value\":\"foo\"},{\"value\":\"foo\"}]"; - List list = mapper.fromJson(json, - ResolvableType.forClassWithGenerics(List.class, Foo.class).getType()); - assertThat(list).hasSize(2); - assertThat(list.get(0).getValue()).isEqualTo("foo"); - assertThat(mapper.toString(list)).isEqualTo(json); - } - - @ParameterizedTest - @MethodSource("params") - public void intArray(JsonMapper mapper) { - List list = mapper.fromJson("[123,456]", - ResolvableType.forClassWithGenerics(List.class, Integer.class).getType()); - assertThat(list).hasSize(2); - assertThat(list.get(0)).isEqualTo(123); - } - - @ParameterizedTest - @MethodSource("params") - public void emptyArray(JsonMapper mapper) { - List list = mapper.fromJson("[]", - ResolvableType.forClassWithGenerics(List.class, Foo.class).getType()); - assertThat(list).hasSize(0); - } - - @ParameterizedTest - @MethodSource("params") - public void vanillaObject(JsonMapper mapper) { - String json = "{\"value\":\"foo\"}"; - Foo foo = mapper.fromJson(json, Foo.class); - assertThat(foo.getValue()).isEqualTo("foo"); - assertThat(mapper.toString(foo)).isEqualTo(json); - } - - @ParameterizedTest - @MethodSource("params") - public void stringRepresentingJson(JsonMapper mapper) { - String json = "{\"value\":\"foo\"}"; - byte[] bytes = mapper.toJson(json); - assertThat(new String(bytes)).isEqualTo(json); - } - - @ParameterizedTest - @MethodSource("params") - public void intValue(JsonMapper mapper) { - int foo = mapper.fromJson("123", Integer.class); - assertThat(foo).isEqualTo(123); - } - - @ParameterizedTest - @MethodSource("params") - public void empty(JsonMapper mapper) { - Foo foo = mapper.fromJson("{}", Foo.class); - assertThat(foo.getValue()).isNull(); - } - - public static class Foo { - - private String value; - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - -} diff --git a/spring-cloud-function-context/src/test/resources/avro/sensor.avsc b/spring-cloud-function-context/src/test/resources/avro/sensor.avsc deleted file mode 100644 index c0e060d3d..000000000 --- a/spring-cloud-function-context/src/test/resources/avro/sensor.avsc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "namespace" : "com.example", - "type" : "record", - "name" : "Sensor", - "fields" : [ - {"name":"id","type":"string"}, - {"name":"temperature", "type":"float", "default":0.0}, - {"name":"acceleration", "type":"float","default":0.0}, - {"name":"velocity","type":"float","default":0.0} - ] -} diff --git a/spring-cloud-function-context/src/test/resources/greeter.jar b/spring-cloud-function-context/src/test/resources/greeter.jar deleted file mode 100644 index 24671c2ba..000000000 Binary files a/spring-cloud-function-context/src/test/resources/greeter.jar and /dev/null differ diff --git a/spring-cloud-function-core/.jdk8 b/spring-cloud-function-core/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-core/pom.xml b/spring-cloud-function-core/pom.xml deleted file mode 100644 index ef244ebc7..000000000 --- a/spring-cloud-function-core/pom.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-core - jar - Spring Cloud Function Core - Spring Cloud Function Core - - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - - - - io.projectreactor - reactor-core - - - org.springframework - spring-core - - - org.springframework.boot - spring-boot-starter-test - test - - - - diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/core/FunctionInvocationHelper.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/core/FunctionInvocationHelper.java deleted file mode 100644 index ccd11c6e2..000000000 --- a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/core/FunctionInvocationHelper.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.springframework.cloud.function.core; - - -/** - * - * @author Oleg Zhurakousky - * @since 3.1 - * - */ -public interface FunctionInvocationHelper { - - default boolean isRetainOuputAsMessage(I input) { - return true; - }; - - default I preProcessInput(I input, Object inputConverter) { - return input; - } - - default Object postProcessResult(Object result, I input) { - return result; - } -} diff --git a/spring-cloud-function-dependencies/pom.xml b/spring-cloud-function-dependencies/pom.xml deleted file mode 100644 index b10724181..000000000 --- a/spring-cloud-function-dependencies/pom.xml +++ /dev/null @@ -1,138 +0,0 @@ - - - 4.0.0 - - spring-cloud-dependencies-parent - org.springframework.cloud - 4.1.0-SNAPSHOT - - - spring-cloud-function-dependencies - 4.1.0-SNAPSHOT - pom - Spring Cloud Function Dependencies - Spring Cloud Function Dependencies - - - - org.springframework.cloud - spring-cloud-function-context - ${project.version} - - - org.springframework.cloud - spring-cloud-function-core - ${project.version} - - - org.springframework.cloud - spring-cloud-function-web - ${project.version} - - - org.springframework.cloud - spring-cloud-starter-function-web - ${project.version} - - - org.springframework.cloud - spring-cloud-starter-function-webflux - ${project.version} - - - org.springframework.cloud - spring-cloud-function-deployer - ${project.version} - - - org.springframework.cloud - spring-cloud-function-adapter-aws - ${project.version} - - - org.springframework.cloud - spring-cloud-function-adapter-azure - ${project.version} - - - org.springframework.cloud - spring-cloud-function-adapter-gcp - ${project.version} - - - org.springframework.cloud - spring-cloud-function-integration - ${project.version} - - - org.springframework.cloud - spring-cloud-function-kotlin - ${project.version} - - - org.springframework.cloud - spring-cloud-function-rsocket - ${project.version} - - - org.springframework.cloud - spring-cloud-function-grpc - ${project.version} - - - org.springframework.cloud - spring-cloud-function-grpc-cloudevent-ext - ${project.version} - - - org.springframework.cloud - spring-cloud-function-serverless-web - ${project.version} - - - - - - spring - - true - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-deployer/.gitignore b/spring-cloud-function-deployer/.gitignore deleted file mode 100644 index a2a3040aa..000000000 --- a/spring-cloud-function-deployer/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/** -!**/src/test/** - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-deployer/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud-function-deployer/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index 72308aa47..000000000 --- a/spring-cloud-function-deployer/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,114 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URL; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; -import java.util.Properties; - -public class MavenWrapperDownloader { - - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = - "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: : " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 01e679973..000000000 Binary files a/spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index cd0d451cc..000000000 --- a/spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip diff --git a/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/0.0.1-SNAPSHOT/demo-stream-0.0.1-SNAPSHOT.jar b/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/0.0.1-SNAPSHOT/demo-stream-0.0.1-SNAPSHOT.jar deleted file mode 100644 index f2c112136..000000000 Binary files a/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/0.0.1-SNAPSHOT/demo-stream-0.0.1-SNAPSHOT.jar and /dev/null differ diff --git a/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/0.0.1-SNAPSHOT/demo-stream-0.0.1-SNAPSHOT.pom b/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/0.0.1-SNAPSHOT/demo-stream-0.0.1-SNAPSHOT.pom deleted file mode 100644 index b6eb34938..000000000 --- a/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/0.0.1-SNAPSHOT/demo-stream-0.0.1-SNAPSHOT.pom +++ /dev/null @@ -1,107 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.3.0.RELEASE - - - - oz.demo - demo-stream - 0.0.1-SNAPSHOT - demo-stream - Demo project for Spring Boot - - - 1.8 - Hoxton.SR5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - - - - - - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud.version} - pom - import - - - - - - - - - - - - - - diff --git a/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/0.0.1-SNAPSHOT/maven-metadata-local.xml b/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/0.0.1-SNAPSHOT/maven-metadata-local.xml deleted file mode 100644 index 98aed06e9..000000000 --- a/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/0.0.1-SNAPSHOT/maven-metadata-local.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - oz.demo - demo-stream - 0.0.1-SNAPSHOT - - - true - - 20200721131233 - - - jar - 0.0.1-SNAPSHOT - 20200721131233 - - - pom - 0.0.1-SNAPSHOT - 20200721131233 - - - - diff --git a/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/maven-metadata-local.xml b/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/maven-metadata-local.xml deleted file mode 100644 index bcc06a525..000000000 --- a/spring-cloud-function-deployer/mavenrepo/oz/demo/demo-stream/maven-metadata-local.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - oz.demo - demo-stream - - - 0.0.1-SNAPSHOT - - 20200721131233 - - diff --git a/spring-cloud-function-deployer/mvnw b/spring-cloud-function-deployer/mvnw deleted file mode 100755 index 8b9da3b8b..000000000 --- a/spring-cloud-function-deployer/mvnw +++ /dev/null @@ -1,286 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - wget "$jarUrl" -O "$wrapperJarPath" - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - curl -o "$wrapperJarPath" "$jarUrl" - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-deployer/mvnw.cmd b/spring-cloud-function-deployer/mvnw.cmd deleted file mode 100644 index fef5a8f7f..000000000 --- a/spring-cloud-function-deployer/mvnw.cmd +++ /dev/null @@ -1,161 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" -FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - echo Found %WRAPPER_JAR% -) else ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" - echo Finished downloading %WRAPPER_JAR% -) -@REM End of extension - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/spring-cloud-function-deployer/pom.xml b/spring-cloud-function-deployer/pom.xml deleted file mode 100644 index b61c18fde..000000000 --- a/spring-cloud-function-deployer/pom.xml +++ /dev/null @@ -1,152 +0,0 @@ - - - 4.0.0 - spring-cloud-function-deployer - jar - spring-cloud-function-deployer - Spring Cloud Function Deployer - - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - - - 17 - - - - - org.springframework.boot - spring-boot-loader - - - org.springframework.boot - spring-boot-starter - - - org.springframework.cloud - spring-cloud-function-context - - - org.springframework.cloud - spring-cloud-deployer-resource-maven - 2.5.1 - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-configuration-processor - true - - - io.cloudevents - cloudevents-spring - 2.2.0 - test - - - - - - - org.apache.maven.plugins - maven-invoker-plugin - 3.0.1 - - ${project.build.directory}/local-repo - - - - - prepare-test - test-compile - - run - - - ${project.build.directory}/it - - src/it/settings.xml - true - true - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.apache.maven.plugins - - - maven-invoker-plugin - - - [3.0.1,) - - - run - - - - - - - - - - - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - - diff --git a/spring-cloud-function-deployer/src/it/bootapp-multi/pom.xml b/spring-cloud-function-deployer/src/it/bootapp-multi/pom.xml deleted file mode 100644 index 5e3f1e2ba..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp-multi/pom.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - 4.0.0 - - function.example - bootapp-multi - 1.0.0.RELEASE - jar - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 17 - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.boot - spring-boot-starter - - - io.projectreactor - reactor-core - 3.1.2.RELEASE - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - exec - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - package - - unpack - - - - - ${project.groupId} - ${project.artifactId} - ${project.version} - exec - - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-deployer/src/it/bootapp-multi/src/main/java/function/example/MyFn.java b/spring-cloud-function-deployer/src/it/bootapp-multi/src/main/java/function/example/MyFn.java deleted file mode 100644 index dd07b1d6d..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp-multi/src/main/java/function/example/MyFn.java +++ /dev/null @@ -1,29 +0,0 @@ -package function.example; - -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuples; - -import java.util.Collections; -import java.util.function.Function; - -public class MyFn implements Function, Flux>, Tuple2, Flux>> { - - - @Override - public Tuple2, Flux> apply(Tuple2, Flux> inputs) { - Flux words = inputs.getT1(); - Flux numbers = inputs.getT2().publish().autoConnect(2); - - - Flux avg = numbers.buffer(2, 1) - .map(l -> l.stream().mapToInt(Integer::intValue).average().getAsDouble()) - .take(3); - - Flux repeated = words.zipWith(numbers) - .flatMap(t -> Flux.fromIterable(Collections.nCopies(t.getT2(), t.getT1()))); - - return Tuples.of(avg, repeated); - - } -} diff --git a/spring-cloud-function-deployer/src/it/bootapp-multi/src/main/java/function/example/RepeaterApplication.java b/spring-cloud-function-deployer/src/it/bootapp-multi/src/main/java/function/example/RepeaterApplication.java deleted file mode 100644 index 73f4d9c94..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp-multi/src/main/java/function/example/RepeaterApplication.java +++ /dev/null @@ -1,80 +0,0 @@ -package function.example; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; - -import java.util.function.Function; - -@SpringBootApplication -public class RepeaterApplication { - - @Bean - public Function, Flux>, Flux> fn() { - return tuple -> { - Flux cartEventStream = tuple.getT1(); - Flux checkoutEventStream = tuple.getT2(); - - return Flux.zip(cartEventStream, checkoutEventStream, (cartEvent, checkoutEvent) -> { - OrderEvent oe = new OrderEvent(); - oe.setOrderEvent(cartEvent.toString() + "- " + checkoutEvent.toString()); - return oe; - }); - }; - } - - public static void main(String[] args) { - SpringApplication.run(RepeaterApplication.class, args); - } - - public static class CartEvent { - private String carEvent; - - public String getCarEvent() { - return carEvent; - } - - public void setCarEvent(String carEvent) { - this.carEvent = carEvent; - } - - public String toString() { - return "CartEvent: " + carEvent; - } - } - - public static class CheckoutEvent { - private String checkoutEvent; - - public String getCheckoutEvent() { - return checkoutEvent; - } - - public void setCheckoutEvent(String checkoutEvent) { - this.checkoutEvent = checkoutEvent; - } - - public String toString() { - return "CheckoutEvent: " + checkoutEvent; - } - } - - public static class OrderEvent { - private String orderEvent; - - public String getOrderEvent() { - return orderEvent; - } - - public void setOrderEvent(String orderEvent) { - this.orderEvent = orderEvent; - } - - public String toString() { - return "OrderEvent: " + orderEvent; - } - } - -} diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-javax/pom.xml b/spring-cloud-function-deployer/src/it/bootapp-with-javax/pom.xml deleted file mode 100644 index 98ff289a4..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp-with-javax/pom.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - 4.0.0 - - function.example - bootapp-with-javax - 1.0.0.RELEASE - jar - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 17 - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.boot - spring-boot-starter - - - com.sun.mail - javax.mail - 1.6.2 - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - exec - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - package - - unpack - - - - - ${project.groupId} - ${project.artifactId} - ${project.version} - exec - - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/SimpleFunctionAppApplication.java b/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/SimpleFunctionAppApplication.java deleted file mode 100644 index ed8f694ef..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/SimpleFunctionAppApplication.java +++ /dev/null @@ -1,57 +0,0 @@ -package function.example; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class SimpleFunctionAppApplication { - - public static void main(String[] args) { - SpringApplication.run(SimpleFunctionAppApplication.class, args); - } - - @Bean - public Function uppercase() { - System.out.println("==> CREATING 'uppercase' FUNCTION bean"); - return new UpperCaseFunction(); - } - - @Bean - public Function uppercasePerson() { - System.out.println("==> CREATING 'uppercasePerson' FUNCTION bean"); - return person -> { - Person p = new Person(); - p.setId(person.getId()); - p.setName(person.getName().toUpperCase()); - return p; - }; - } - - - - public static class Person { - private String name; - - private int id; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - } -} diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/UpperCaseFunction.java deleted file mode 100644 index 735ad65fb..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/UpperCaseFunction.java +++ /dev/null @@ -1,23 +0,0 @@ -package function.example; - -import java.util.function.Function; - -import javax.mail.Address; -import javax.mail.internet.AddressException; -import javax.mail.internet.InternetAddress; - -public class UpperCaseFunction implements Function { - - @Override - public String apply(String value) { - System.out.println("Uppercasing " + value); - try { - Address address = new InternetAddress(value); - } - catch (AddressException e) { - throw new IllegalStateException("Failed to create and address: ", e); - } - return value.toUpperCase(); - } - -} diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-scf/pom.xml b/spring-cloud-function-deployer/src/it/bootapp-with-scf/pom.xml deleted file mode 100644 index f82f988c3..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp-with-scf/pom.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - 4.0.0 - - function.example - bootapp-with-scf - 1.0.0.RELEASE - jar - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 17 - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.boot - spring-boot-starter - - - org.springframework.cloud - spring-cloud-function-context - ${spring-cloud-function.version} - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - exec - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - package - - unpack - - - - - ${project.groupId} - ${project.artifactId} - ${project.version} - exec - - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/SimpleFunctionAppApplication.java b/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/SimpleFunctionAppApplication.java deleted file mode 100644 index 63e0719c3..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/SimpleFunctionAppApplication.java +++ /dev/null @@ -1,63 +0,0 @@ -package function.example; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -@SpringBootApplication -public class SimpleFunctionAppApplication { - - public static void main(String[] args) { - SpringApplication.run(SimpleFunctionAppApplication.class, args); - } - - @Bean - public Function, Message> uppercase() { - System.out.println("==> CREATING 'uppercase' FUNCTION bean"); - return message -> { - UpperCaseFunction func = new UpperCaseFunction(); - String result = func.apply(message.getPayload()); - return MessageBuilder.withPayload(result).build(); - }; - } - - @Bean - public Function uppercasePerson() { - System.out.println("==> CREATING 'uppercasePerson' FUNCTION bean"); - return person -> { - Person p = new Person(); - p.setId(person.getId()); - p.setName(person.getName().toUpperCase()); - return p; - }; - } - - - - public static class Person { - private String name; - - private int id; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - } -} diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/UpperCaseFunction.java deleted file mode 100644 index 859a54a58..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/UpperCaseFunction.java +++ /dev/null @@ -1,13 +0,0 @@ -package function.example; - -import java.util.function.Function; - -public class UpperCaseFunction implements Function { - - @Override - public String apply(String value) { - System.out.println("Uppercasing " + value); - return value.toUpperCase(); - } - -} diff --git a/spring-cloud-function-deployer/src/it/bootapp/pom.xml b/spring-cloud-function-deployer/src/it/bootapp/pom.xml deleted file mode 100644 index 64a0da2fe..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp/pom.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - 4.0.0 - - function.example - bootapp - 1.0.0.RELEASE - jar - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 17 - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.boot - spring-boot-starter - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - exec - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - package - - unpack - - - - - ${project.groupId} - ${project.artifactId} - ${project.version} - exec - - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java b/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java deleted file mode 100644 index ed8f694ef..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java +++ /dev/null @@ -1,57 +0,0 @@ -package function.example; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class SimpleFunctionAppApplication { - - public static void main(String[] args) { - SpringApplication.run(SimpleFunctionAppApplication.class, args); - } - - @Bean - public Function uppercase() { - System.out.println("==> CREATING 'uppercase' FUNCTION bean"); - return new UpperCaseFunction(); - } - - @Bean - public Function uppercasePerson() { - System.out.println("==> CREATING 'uppercasePerson' FUNCTION bean"); - return person -> { - Person p = new Person(); - p.setId(person.getId()); - p.setName(person.getName().toUpperCase()); - return p; - }; - } - - - - public static class Person { - private String name; - - private int id; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - } -} diff --git a/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java deleted file mode 100644 index 859a54a58..000000000 --- a/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java +++ /dev/null @@ -1,13 +0,0 @@ -package function.example; - -import java.util.function.Function; - -public class UpperCaseFunction implements Function { - - @Override - public String apply(String value) { - System.out.println("Uppercasing " + value); - return value.toUpperCase(); - } - -} diff --git a/spring-cloud-function-deployer/src/it/bootjar-multi/pom.xml b/spring-cloud-function-deployer/src/it/bootjar-multi/pom.xml deleted file mode 100644 index 227adef52..000000000 --- a/spring-cloud-function-deployer/src/it/bootjar-multi/pom.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - 4.0.0 - - function.example - bootjar-multi - 1.0.0.RELEASE - jar - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 17 - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.boot - spring-boot-starter - - - io.projectreactor - reactor-core - 3.1.2.RELEASE - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - NONE - exec - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - package - - unpack - - - - - ${project.groupId} - ${project.artifactId} - ${project.version} - exec - - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-deployer/src/it/bootjar-multi/src/main/java/function/example/Repeater.java b/spring-cloud-function-deployer/src/it/bootjar-multi/src/main/java/function/example/Repeater.java deleted file mode 100644 index 643b9475d..000000000 --- a/spring-cloud-function-deployer/src/it/bootjar-multi/src/main/java/function/example/Repeater.java +++ /dev/null @@ -1,27 +0,0 @@ -package function.example; - -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuples; - -import java.util.Collections; -import java.util.function.Function; - -public class Repeater implements Function, Flux>, Tuple2, Flux>> { - - @Override - public Tuple2, Flux> apply(Tuple2, Flux> inputs) { - Flux stringFlux = inputs.getT1(); - Flux integerFlux = inputs.getT2(); - Flux sharedIntFlux = integerFlux.publish().autoConnect(2); - - Flux repeated = stringFlux.zipWith(sharedIntFlux) - .flatMap(t -> Flux.fromIterable(Collections.nCopies(t.getT2(), t.getT1()))); - - Flux sum = sharedIntFlux.buffer(2, 1) - .map(l -> l.stream().mapToInt(Integer::intValue).sum()) - ; - - return Tuples.of(repeated, sum); - } -} diff --git a/spring-cloud-function-deployer/src/it/bootjar/pom.xml b/spring-cloud-function-deployer/src/it/bootjar/pom.xml deleted file mode 100644 index 6231f61a4..000000000 --- a/spring-cloud-function-deployer/src/it/bootjar/pom.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - 4.0.0 - - function.example - bootjar - 1.0.0.RELEASE - jar - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 17 - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.boot - spring-boot-starter - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - exec - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - package - - unpack - - - - - ${project.groupId} - ${project.artifactId} - ${project.version} - exec - - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/ReverseFunction.java b/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/ReverseFunction.java deleted file mode 100644 index 090a28e2e..000000000 --- a/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/ReverseFunction.java +++ /dev/null @@ -1,13 +0,0 @@ -package function.example; - -import java.util.function.Function; - -public class ReverseFunction implements Function { - - @Override - public String apply(String value) { - System.out.println("Reversing " + value); - return new StringBuilder(value).reverse().toString(); - } - -} diff --git a/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/SimpleFunctionAppApplication.java b/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/SimpleFunctionAppApplication.java deleted file mode 100644 index 5d5eaf3ea..000000000 --- a/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/SimpleFunctionAppApplication.java +++ /dev/null @@ -1,15 +0,0 @@ -package function.example; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -public class SimpleFunctionAppApplication { - - public static void main(String[] args) { - SpringApplication.run(SimpleFunctionAppApplication.class, args); - } -} diff --git a/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java deleted file mode 100644 index 859a54a58..000000000 --- a/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java +++ /dev/null @@ -1,13 +0,0 @@ -package function.example; - -import java.util.function.Function; - -public class UpperCaseFunction implements Function { - - @Override - public String apply(String value) { - System.out.println("Uppercasing " + value); - return value.toUpperCase(); - } - -} diff --git a/spring-cloud-function-deployer/src/it/bootjarnostart/pom.xml b/spring-cloud-function-deployer/src/it/bootjarnostart/pom.xml deleted file mode 100644 index 42cb49ef7..000000000 --- a/spring-cloud-function-deployer/src/it/bootjarnostart/pom.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - 4.0.0 - - function.example - bootjarnostart - 1.0.0.RELEASE - jar - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 17 - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.boot - spring-boot-starter - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - NONE - exec - - - - org.apache.maven.plugins - maven-jar-plugin - - - - function.example.UpperCaseFunction - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - package - - unpack - - - - - ${project.groupId} - ${project.artifactId} - ${project.version} - exec - - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-deployer/src/it/bootjarnostart/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootjarnostart/src/main/java/function/example/UpperCaseFunction.java deleted file mode 100644 index 859a54a58..000000000 --- a/spring-cloud-function-deployer/src/it/bootjarnostart/src/main/java/function/example/UpperCaseFunction.java +++ /dev/null @@ -1,13 +0,0 @@ -package function.example; - -import java.util.function.Function; - -public class UpperCaseFunction implements Function { - - @Override - public String apply(String value) { - System.out.println("Uppercasing " + value); - return value.toUpperCase(); - } - -} diff --git a/spring-cloud-function-deployer/src/it/settings.xml b/spring-cloud-function-deployer/src/it/settings.xml deleted file mode 100644 index e1e0ace34..000000000 --- a/spring-cloud-function-deployer/src/it/settings.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - it-repo - - true - - - - local.central - @localRepositoryUrl@ - - true - - - true - - - - - - local.central - @localRepositoryUrl@ - - true - - - true - - - - - - diff --git a/spring-cloud-function-deployer/src/it/simplestjar/pom.xml b/spring-cloud-function-deployer/src/it/simplestjar/pom.xml deleted file mode 100644 index 8eaa026d9..000000000 --- a/spring-cloud-function-deployer/src/it/simplestjar/pom.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - 4.0.0 - - function.example - simplestjar - 1.0.0.RELEASE - jar - - simplestjar - - - UTF-8 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - package - - shade - - - - - - - - - - io.cloudevents - cloudevents-api - 2.2.0 - true - - - - - diff --git a/spring-cloud-function-deployer/src/it/simplestjar/src/main/java/function/example/EchoCloudEventFunction.java b/spring-cloud-function-deployer/src/it/simplestjar/src/main/java/function/example/EchoCloudEventFunction.java deleted file mode 100644 index 0dc913143..000000000 --- a/spring-cloud-function-deployer/src/it/simplestjar/src/main/java/function/example/EchoCloudEventFunction.java +++ /dev/null @@ -1,16 +0,0 @@ -package function.example; - -import java.util.Map; -import java.util.function.Function; - -import io.cloudevents.CloudEvent; - -public class EchoCloudEventFunction implements Function { - - @Override - public CloudEvent apply(CloudEvent value) { - System.out.println("Received " + value); - return value; - } - -} diff --git a/spring-cloud-function-deployer/src/it/simplestjarcs/pom.xml b/spring-cloud-function-deployer/src/it/simplestjarcs/pom.xml deleted file mode 100644 index 503556277..000000000 --- a/spring-cloud-function-deployer/src/it/simplestjarcs/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - 4.0.0 - - function.example - simplestjarcs - 1.0.0.RELEASE - Showcases compoinent scanning capabilities - jar - - simplestjarcs - - - UTF-8 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - - - - - - - diff --git a/spring-cloud-function-deployer/src/it/simplestjarcs/src/main/java/functions/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/simplestjarcs/src/main/java/functions/UpperCaseFunction.java deleted file mode 100644 index 07f535dc1..000000000 --- a/spring-cloud-function-deployer/src/it/simplestjarcs/src/main/java/functions/UpperCaseFunction.java +++ /dev/null @@ -1,13 +0,0 @@ -package functions; - -import java.util.function.Function; - -public class UpperCaseFunction implements Function { - - @Override - public String apply(String value) { - System.out.println("Uppercasing " + value); - return value.toUpperCase(); - } - -} diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/DeployerContextUtils.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/DeployerContextUtils.java deleted file mode 100644 index 070328b0d..000000000 --- a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/DeployerContextUtils.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.deployer; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.List; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.ResolvableType; -import org.springframework.core.io.Resource; -import org.springframework.core.type.MethodMetadata; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; - -/** - * @author Oleg Zhurakousky - * @since 3.0 - */ -abstract class DeployerContextUtils { - - public static Type findType(BeanFactory beanFactory, String name) { - ConfigurableListableBeanFactory registry = (ConfigurableListableBeanFactory) beanFactory; - AbstractBeanDefinition definition = (AbstractBeanDefinition) registry.getBeanDefinition(name); - - Object source = definition.getSource(); - - Type param = null; - if (source instanceof MethodMetadata) { - param = findBeanType(definition, ((MethodMetadata) source).getDeclaringClassName(), ((MethodMetadata) source).getMethodName()); - } - else if (source instanceof Resource) { - param = registry.getType(name); - } - else { - ResolvableType type = (ResolvableType) getField(definition, "targetType"); - if (type != null) { - param = type.getType(); - } - } - return param; - } - - private static Type findBeanType(AbstractBeanDefinition definition, String declaringClassName, String methodName) { - Class factory = ClassUtils.resolveClassName(declaringClassName, null); - Class[] params = getParamTypes(factory, definition); - Method method = ReflectionUtils.findMethod(factory, methodName, - params); - Type type = method.getGenericReturnType(); - return type; - } - - private static Class[] getParamTypes(Class factory, - AbstractBeanDefinition definition) { - if (definition instanceof RootBeanDefinition) { - RootBeanDefinition root = (RootBeanDefinition) definition; - for (Method method : getCandidateMethods(factory, root)) { - if (root.isFactoryMethod(method)) { - return method.getParameterTypes(); - } - } - } - List> params = new ArrayList<>(); - for (ConstructorArgumentValues.ValueHolder holder : definition - .getConstructorArgumentValues().getIndexedArgumentValues().values()) { - params.add(ClassUtils.resolveClassName(holder.getType(), null)); - } - return params.toArray(new Class[0]); - } - - private static Method[] getCandidateMethods(final Class factoryClass, - final RootBeanDefinition mbd) { - if (System.getSecurityManager() != null) { - return AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Method[] run() { - return (mbd.isNonPublicAccessAllowed() - ? ReflectionUtils.getAllDeclaredMethods(factoryClass) - : factoryClass.getMethods()); - } - }); - } - else { - return (mbd.isNonPublicAccessAllowed() - ? ReflectionUtils.getAllDeclaredMethods(factoryClass) - : factoryClass.getMethods()); - } - } - - private static Object getField(Object target, String name) { - Field field = ReflectionUtils.findField(target.getClass(), name); - if (field == null) { - return null; - } - ReflectionUtils.makeAccessible(field); - return ReflectionUtils.getField(field, target); - } - -} diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionArchiveDeployer.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionArchiveDeployer.java deleted file mode 100644 index 2c96fdd64..000000000 --- a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionArchiveDeployer.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.deployer; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.regex.Pattern; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.loader.JarLauncher; -import org.springframework.boot.loader.LaunchedURLClassLoader; -import org.springframework.boot.loader.archive.Archive; -import org.springframework.boot.loader.jar.JarFile; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; -import org.springframework.core.type.filter.RegexPatternTypeFilter; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.expression.spel.support.StandardTypeLocator; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.ReflectionUtils.MethodCallback; -import org.springframework.util.ReflectionUtils.MethodFilter; -import org.springframework.util.StreamUtils; -import org.springframework.util.StringUtils; - -/** - * - * @author Oleg Zhurakousky - * @since 3.0 - * - */ -class FunctionArchiveDeployer extends JarLauncher { - - private static Log logger = LogFactory.getLog(FunctionArchiveDeployer.class); - - private final StandardEvaluationContext evalContext = new StandardEvaluationContext(); - - private LaunchedURLClassLoader archiveLoader; - - FunctionArchiveDeployer(Archive archive) { - super(archive); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - void deploy(FunctionRegistry functionRegistry, FunctionDeployerProperties functionProperties, String[] args, ApplicationContext applicationContext) { - ClassLoader currentLoader = Thread.currentThread().getContextClassLoader(); - - try { - ClassLoader cl = createClassLoader(discoverClassPathAcrhives().iterator()); - - Thread.currentThread().setContextClassLoader(cl); - - - evalContext.setTypeLocator(new StandardTypeLocator(Thread.currentThread().getContextClassLoader())); - - if (this.isBootApplicationWithMain()) { - this.launchFunctionArchive(args); - - Map functions = this.discoverBeanFunctions(); - if (logger.isInfoEnabled() && !CollectionUtils.isEmpty(functions)) { - logger.info("Discovered functions in deployed application context: " + functions); - } - for (Entry entry : functions.entrySet()) { - FunctionRegistration registration = new FunctionRegistration(entry.getValue(), entry.getKey()); - Type type = this.discoverFunctionType(entry.getKey()); - if (logger.isInfoEnabled()) { - logger.info("Registering function '" + entry.getKey() + "' of type '" + type - + "' in FunctionRegistry."); - } - registration.type(type); - functionRegistry.register(registration); - } - } - - String[] functionClassNames = discoverFunctionClassName(functionProperties); - - if (functionClassNames == null) { - ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner((BeanDefinitionRegistry) applicationContext, false); - scanner.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*"))); - Set findCandidateComponents = scanner.findCandidateComponents("functions"); - - ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); - for (BeanDefinition beanDefinition : findCandidateComponents) { - String className = beanDefinition.getBeanClassName(); - Class functionClass = currentClassLoader.loadClass(className); - if (Function.class.isAssignableFrom(functionClass) || Supplier.class.isAssignableFrom(functionClass) || Consumer.class.isAssignableFrom(functionClass)) { - FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(className); - if (registration != null) { - functionRegistry.register(registration); - } - } - } - } - else { - for (String functionClassName : functionClassNames) { - if (StringUtils.hasText(functionClassName)) { - FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(functionClassName); - if (registration != null) { - functionRegistry.register(registration); - } - } - } - } - } - catch (Exception e) { - throw new IllegalStateException("Failed to deploy archive " + this.getArchive(), e); - } - finally { - Thread.currentThread().setContextClassLoader(currentLoader); - } - } - - void undeploy() { - this.stopDeployedApplicationContext(); - try { - this.archiveLoader.close(); - logger.info("Closed archive class loader"); - } - catch (IOException e) { - logger.error("Failed to closed archive class loader", e); - } - } - - @Override - protected ClassLoader createClassLoader(URL[] urls) throws Exception { - String classAsPath = DeployerContextUtils.class.getName().replace('.', '/') + ".class"; - byte[] deployerContextUtilsBytes = StreamUtils - .copyToByteArray(DeployerContextUtils.class.getClassLoader().getResourceAsStream(classAsPath)); - /* - * While LaunchedURLClassLoader is completely disconnected with the current - * class loader, this will ensure that certain classes (e.g., org.reactivestreams.* see #shouldLoadViaDeployerLoader() ) - * are shared across two class loaders. - */ - final ClassLoader deployerClassLoader = getClass().getClassLoader(); - this.archiveLoader = new LaunchedURLClassLoader(urls, deployerClassLoader.getParent()) { - @Override - public Class loadClass(String name) throws ClassNotFoundException { - Class clazz = null; - if (shouldLoadViaDeployerLoader(name)) { - clazz = deployerClassLoader.loadClass(name); - } - else if (name.equals(DeployerContextUtils.class.getName())) { - /* - * This will ensure that `DeployerContextUtils` is available to - * foreign class loader for cases where foreign JAR does not - * have SCF dependencies. - */ - try { - clazz = super.loadClass(name, false); - } - catch (Exception e) { - clazz = defineClass(name, deployerContextUtilsBytes, 0, deployerContextUtilsBytes.length); - } - } - else { - clazz = super.loadClass(name, false); - } - return clazz; - } - }; - return this.archiveLoader; - } - - private boolean shouldLoadViaDeployerLoader(String name) { - return name.startsWith("org.reactivestreams") - || name.startsWith("reactor.") - || name.startsWith("io.cloudevents") - || name.startsWith("org.springframework.messaging.Message") - || name.startsWith("org.springframework.messaging.converter.MessageConverter"); - } - - - - private String[] discoverFunctionClassName(FunctionDeployerProperties functionProperties) { - try { - if (StringUtils.hasText(functionProperties.getFunctionClass())) { - return functionProperties.getFunctionClass().split(";"); - } - else if (StringUtils.hasText(this.getArchive().getManifest().getMainAttributes().getValue("Function-Class"))) { - return new String[] {this.getArchive().getManifest().getMainAttributes().getValue("Function-Class")}; - } - else { - return null; - } - } - catch (Exception e) { - throw new IllegalStateException("Failed to discover function class name", e); - } - } - - private boolean isBootApplicationWithMain() { - try { - if (this.getArchive().getManifest() == null) { - return false; - } - return StringUtils.hasText(this.getArchive().getManifest().getMainAttributes().getValue("Start-Class")); - } - catch (Exception e) { - throw new IllegalStateException(e); - } - } - - private List discoverClassPathAcrhives() throws Exception { - Iterator iter = this.getClassPathArchivesIterator(); - List classPathArchives = new ArrayList<>(); - while (iter.hasNext()) { - classPathArchives.add(iter.next()); - } - - if (CollectionUtils.isEmpty(classPathArchives)) { - classPathArchives.add(this.getArchive()); - } - return classPathArchives; - } - - private FunctionRegistration discovereAndLoadFunctionFromClassName(String functionClassName) throws Exception { - FunctionRegistration functionRegistration = null; - AtomicReference typeRef = new AtomicReference<>(); - Class functionClass = Thread.currentThread().getContextClassLoader().loadClass(functionClassName); - - ReflectionUtils.doWithMethods(functionClass, new MethodCallback() { - @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - typeRef.set(FunctionTypeUtils.discoverFunctionTypeFromFunctionMethod(method)); - } - }, new MethodFilter() { - @Override - public boolean matches(Method method) { - String name = method.getName(); - return typeRef.get() == null && !method.isBridge() - && ("apply".equals(name) || "accept".equals(name) || "get".equals(name)); - } - }); - - if (typeRef.get() != null) { - Object functionInstance = functionClass.newInstance(); - String functionName = StringUtils.uncapitalize(functionClass.getSimpleName()); - if (logger.isInfoEnabled()) { - logger.info("Registering function class '" + functionClass + "' of type '" + typeRef.get() - + "' under name '" + functionName + "'."); - } - functionRegistration = new FunctionRegistration<>(functionInstance, functionName); - functionRegistration.type(typeRef.get()); - } - return functionRegistration; - } - - private void launchFunctionArchive(String[] args) throws Exception { - JarFile.registerUrlProtocolHandler(); - - String mainClassName = getMainClass(); - Class mainClass = Thread.currentThread().getContextClassLoader().loadClass(mainClassName); - - Class bootAppClass = Thread.currentThread().getContextClassLoader() - .loadClass(SpringApplication.class.getName()); - Method runMethod = bootAppClass.getDeclaredMethod("run", Class.class, String[].class); - Object applicationContext = runMethod.invoke(null, mainClass, args); - if (logger.isInfoEnabled()) { - logger.info("Application context for archive '" + this.getArchive().getUrl() + "' is created."); - } - evalContext.setVariable("context", applicationContext); - setBeanFactory(applicationContext); - } - - private void setBeanFactory(Object applicationContext) { - Expression parsed = new SpelExpressionParser().parseExpression("#context.getBeanFactory()"); - Object beanFactory = parsed.getValue(this.evalContext); - evalContext.setVariable("bf", beanFactory); - } - - private Type discoverFunctionType(String name) { - evalContext.setVariable("functionName", name); - String expr = "T(" + DeployerContextUtils.class.getName() + ").findType(#bf, #functionName)"; - Expression parsed = new SpelExpressionParser().parseExpression(expr); - Object type = parsed.getValue(this.evalContext); - return (Type) type; - } - - private void stopDeployedApplicationContext() { - if (evalContext.lookupVariable("context") != null) { // no start-class uber jars - Expression parsed = new SpelExpressionParser().parseExpression("#context.stop()"); - parsed.getValue(this.evalContext); - } - } - - @SuppressWarnings("unchecked") - private Map discoverBeanFunctions() { - Map allFunctions = new HashMap(); - if (evalContext.lookupVariable("context") != null) { // no start-class uber jars - Expression parsed = new SpelExpressionParser() - .parseExpression("#context.getBeansOfType(T(java.util.function.Function))"); - allFunctions.putAll((Map) parsed.getValue(this.evalContext)); - parsed = new SpelExpressionParser().parseExpression("#context.getBeansOfType(T(java.util.function.Supplier))"); - allFunctions.putAll((Map) parsed.getValue(this.evalContext)); - parsed = new SpelExpressionParser().parseExpression("#context.getBeansOfType(T(java.util.function.Consumer))"); - allFunctions.putAll((Map) parsed.getValue(this.evalContext)); - } - return allFunctions; - } -} diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerConfiguration.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerConfiguration.java deleted file mode 100644 index 2105c5b0d..000000000 --- a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerConfiguration.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.deployer; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.DefaultApplicationArguments; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.boot.loader.archive.Archive; -import org.springframework.boot.loader.archive.ExplodedArchive; -import org.springframework.boot.loader.archive.JarFileArchive; -import org.springframework.cloud.deployer.resource.maven.MavenProperties; -import org.springframework.cloud.deployer.resource.maven.MavenResourceLoader; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.context.ApplicationContext; -import org.springframework.context.SmartLifecycle; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertiesPropertySource; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * - * Configuration class which creates an instance of {@link SmartLifecycle} - * which deploys and un-deploys packages archives via it's {@link SmartLifecycle#start()} - * and {@link SmartLifecycle#stop()} operations. - *
- * @author Oleg Zhurakousky - * @author Eric Bottard - * - * @since 3.0 - * - */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(FunctionDeployerProperties.class) -@ConditionalOnProperty(name = FunctionProperties.PREFIX + ".location") -public class FunctionDeployerConfiguration { - - private static Log logger = LogFactory.getLog(FunctionDeployerConfiguration.class); - - @Bean - SmartLifecycle functionArchiveUnDeployer(FunctionDeployerProperties functionProperties, - FunctionRegistry functionRegistry, ApplicationArguments arguments, @Nullable MavenProperties mavenProperties, ApplicationContext applicationContext) { - - ApplicationArguments updatedArguments = this.updateArguments(arguments); - - Archive archive = null; - try { - File file; - String location = functionProperties.getLocation(); - Assert.hasText(location, "`spring.cloud.function.location` property must be defined."); - if (location.startsWith("maven://")) { - MavenResourceLoader resourceLoader = new MavenResourceLoader(mavenProperties); - file = resourceLoader.getResource(location).getFile(); - } - else { - file = new File(location); - } - - if (!file.exists()) { - throw new IllegalStateException("Failed to create archive: " + functionProperties.getLocation() + " does not exist"); - } - else if (file.isDirectory()) { - archive = new ExplodedArchive(file); - } - else { - archive = new JarFileArchive(file); - } - } - catch (IOException e) { - throw new IllegalStateException("Failed to create archive: " + functionProperties.getLocation(), e); - } - FunctionArchiveDeployer deployer = new FunctionArchiveDeployer(archive); - - if (logger.isInfoEnabled()) { - logger.info("Deploying archive: " + functionProperties.getLocation()); - } - deployer.deploy(functionRegistry, functionProperties, updatedArguments.getSourceArgs(), applicationContext); - if (logger.isInfoEnabled()) { - logger.info("Successfully deployed archive: " + functionProperties.getLocation()); - } - - return new SmartLifecycle() { - - private boolean running = true; - - @Override - public void stop() { - if (logger.isInfoEnabled()) { - logger.info("Undeploying archive: " + functionProperties.getLocation()); - } - deployer.undeploy(); - if (logger.isInfoEnabled()) { - logger.info("Successfully undeployed archive: " + functionProperties.getLocation()); - } - this.running = false; - } - - @Override - public void start() { - // no op - } - - @Override - public boolean isRunning() { - return this.running; - } - - @Override - public int getPhase() { - return Integer.MAX_VALUE - 1000; - } - }; - - } - - /* - * We need to update the actual arguments with non-legacy properties before passing these arguments to the deployable archive. - * For the current application FunctionProperties already updated and set as a result of EnvironmentPostProcessor - */ - private ApplicationArguments updateArguments(ApplicationArguments arguments) { - List originalArguments = new ArrayList(Arrays.asList(arguments.getSourceArgs())); - - if (arguments.containsOption("function.name")) { - originalArguments.add(FunctionProperties.PREFIX + ".definition=" + arguments.getOptionValues("function.name").get(0)); - } - if (arguments.containsOption("function.location")) { - originalArguments.add(FunctionProperties.PREFIX + ".location=" + arguments.getOptionValues("function.location").get(0)); - } - ApplicationArguments updatedArguments = new DefaultApplicationArguments(originalArguments.toArray(new String[] {})); - return updatedArguments; - } - - - /** - * Instance of {@link EnvironmentPostProcessor} which ensures that legacy - * Function property names are still honored. - */ - static class LegacyPropertyEnvironmentPostProcessor implements EnvironmentPostProcessor { - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { - String functionName = environment.containsProperty("function.name") ? environment.getProperty("function.name") : null; - String functionLocation = environment.containsProperty("function.location") ? environment.getProperty("function.location") : null; - if (StringUtils.hasText(functionName) || StringUtils.hasText(functionLocation)) { - MutablePropertySources propertySources = environment.getPropertySources(); - propertySources.forEach(ps -> { - if (ps instanceof PropertiesPropertySource) { - ((MapPropertySource) ps).getSource().put(FunctionProperties.PREFIX + ".definition", functionName); - ((MapPropertySource) ps).getSource().put(FunctionProperties.PREFIX + ".location", functionLocation); - } - }); - } - } - } - -} diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerProperties.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerProperties.java deleted file mode 100644 index e5e61c779..000000000 --- a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionDeployerProperties.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.deployer; - -import jakarta.annotation.PostConstruct; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.util.Assert; - - -/** - * Configuration properties for deciding how to locate the functional class to execute. - * - * @author Eric Bottard - * @author Oleg Zhurakousky - * - * @see FunctionProperties - */ -@ConfigurationProperties(prefix = FunctionProperties.PREFIX) -public class FunctionDeployerProperties { - - /** - * Location of jar archive containing the supplier/function/consumer class or bean to run. - */ - private String location; - - /** - * The name of the function class to be instantiated and loaded into FunctionCatalog. The name of the - * function will be decapitalized simple name of this class. - */ - private String functionClass; - - public void setFunctionClass(String functionClass) { - this.functionClass = functionClass; - } - - public String getFunctionClass() { - return this.functionClass; - } - - public String getLocation() { - return this.location; - } - - public void setLocation(String location) { - this.location = location; - } - - @PostConstruct - public void init() { - Assert.notNull(this.location, "No archive location provided, please configure spring.cloud.function.location as a jar or directory."); - } - -} diff --git a/spring-cloud-function-deployer/src/main/resources/META-INF/spring.factories b/spring-cloud-function-deployer/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 55304d26b..000000000 --- a/spring-cloud-function-deployer/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=org.springframework.cloud.function.deployer.FunctionDeployerConfiguration$LegacyPropertyEnvironmentPostProcessor diff --git a/spring-cloud-function-deployer/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-function-deployer/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index c792ab9e6..000000000 --- a/spring-cloud-function-deployer/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -org.springframework.cloud.function.deployer.FunctionDeployerConfiguration diff --git a/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java b/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java deleted file mode 100644 index c9ae26965..000000000 --- a/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/FunctionDeployerTests.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright 2017-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.deployer; - - - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuples; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.deployer.resource.maven.MavenProperties; -import org.springframework.cloud.function.cloudevent.CloudEventMessageBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; - - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * @since 3.0 - */ -public class FunctionDeployerTests { - - @BeforeEach - public void before() { - System.clearProperty("spring.cloud.function.definition"); - } - - @Test - public void testWithMavenConfiguration() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=maven://oz.demo:demo-stream:0.0.1-SNAPSHOT", - "--spring.cloud.function.function-class=oz.demo.demostream.MyFunction" }; - - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function function = catalog.lookup("myFunction"); - - assertThat(function.apply("bob")).isEqualTo("BOB"); - } - - /* - * Target function `class UpperCaseFunction implements Function` - * Main/Start class present, no Spring configuration - */ - @Test - public void testWithMainAndStartClassNoSpringConfiguration() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootjar/target/bootjar-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-class=function.example.UpperCaseFunction;function.example.ReverseFunction" }; - - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function function = catalog.lookup("upperCaseFunction"); - - assertThat(function.apply("bob")).isEqualTo("BOB"); - assertThat(function.apply("stacy")).isEqualTo("STACY"); - - function = catalog.lookup("reverseFunction"); - - assertThat(function.apply("bob")).isEqualTo("bob"); - assertThat(function.apply("stacy")).isEqualTo("ycats"); - - Function, Flux> functionAsFlux = catalog.lookup("upperCaseFunction"); - - List results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block(); - assertThat(results.get(0)).isEqualTo("BOB"); - assertThat(results.get(1)).isEqualTo("STACY"); - - functionAsFlux = catalog.lookup("reverseFunction"); - - results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block(); - - assertThat(results.get(0)).isEqualTo("bob"); - assertThat(results.get(1)).isEqualTo("ycats"); - } - - @Test - public void testWithSimplestJar() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar", - "--spring.cloud.function.function-class=function.example.EchoCloudEventFunction" }; - - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function, Message> function = catalog.lookup("echoCloudEventFunction"); - - String data = "{\"name\":\"Ricky\"}"; - Message inputMessage = CloudEventMessageBuilder - .withData(data) - .setId("123") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") - .setSource("https://spring.io/") - .setType("org.springframework") - .build(); - - assertThat(new String(function.apply(inputMessage).getPayload())).isEqualTo(data); - } - - @Test - public void testWithSimplestJarComponentScanning() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/simplestjarcs/target/simplestjarcs-1.0.0.RELEASE.jar"}; - - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function function = catalog.lookup("upperCaseFunction"); - - assertThat(function.apply("bob")).isEqualTo("BOB"); - assertThat(function.apply("stacy")).isEqualTo("STACY"); - - Function, Flux> functionAsFlux = catalog.lookup("upperCaseFunction"); - - List results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block(); - assertThat(results.get(0)).isEqualTo("BOB"); - assertThat(results.get(1)).isEqualTo("STACY"); - } - - @Test - public void testWithSimplestJarExploaded() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/simplestjar/target/classes", - "--spring.cloud.function.function-class=function.example.EchoCloudEventFunction" }; - - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function, Message> function = catalog.lookup("echoCloudEventFunction"); - - String data = "{\"name\":\"Ricky\"}"; - Message inputMessage = CloudEventMessageBuilder - .withData(data) - .setId("123") - .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") - .setSource("https://spring.io/") - .setType("org.springframework") - .build(); - - assertThat(new String(function.apply(inputMessage).getPayload())).isEqualTo(data); - - Function>, Flux>> functionAsFlux = catalog.lookup("echoCloudEventFunction"); - - List> results = functionAsFlux.apply(Flux.just(inputMessage)).collectList().block(); - assertThat(results.get(0).getPayload()).isEqualTo(data.getBytes()); - //assertThat(results.get(1)).isEqualTo("STACY"); - } - - /* - * Target function `class UpperCaseFunction implements Function` - * No Main/Start class present, no Spring configuration - */ - @Test - public void testNoMainAndNoStartClassAndNoSpringConfiguration() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootjarnostart/target/bootjarnostart-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-class=function.example.UpperCaseFunction" }; - - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function function = catalog.lookup("upperCaseFunction"); - - assertThat(function.apply("bob")).isEqualTo("BOB"); - assertThat(function.apply("stacy")).isEqualTo("STACY"); - - Function, Flux> functionAsFlux = catalog.lookup("upperCaseFunction"); - - List results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block(); - assertThat(results.get(0)).isEqualTo("BOB"); - assertThat(results.get(1)).isEqualTo("STACY"); - } - - /* - * Target function `class UpperCaseFunction implements Function` - * No Main/Start class present, no Spring configuration - * - * Function class is discovered via 'Function-Class` manifest entry - */ - @Test - public void testNoMainAndNoStartClassAndNoSpringConfigurationDiscoverClassFromManifest() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootjarnostart/target/bootjarnostart-1.0.0.RELEASE-exec.jar" }; - - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function function = catalog.lookup("upperCaseFunction"); - - assertThat(function.apply("bob")).isEqualTo("BOB"); - assertThat(function.apply("stacy")).isEqualTo("STACY"); - - Function, Flux> functionAsFlux = catalog.lookup("upperCaseFunction"); - - List results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block(); - assertThat(results.get(0)).isEqualTo("BOB"); - assertThat(results.get(1)).isEqualTo("STACY"); - } - - /* - * Target function: - * - * @Bean public Function uppercase() - */ - @Test - public void testWithMainAndStartClassAndSpringConfiguration() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.definition=uppercase" }; - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function, Message> function = catalog.lookup("uppercase", "application/json"); - - Message result = function - .apply(MessageBuilder.withPayload("\"bob\"".getBytes(StandardCharsets.UTF_8)).build()); - assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("\"BOB\""); - } - - @Test - public void testWithLegacyProperties() throws Exception { - String[] args = new String[] { - "--function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar", - "--function.name=uppercase" }; - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function, Message> function = catalog.lookup("uppercase", "application/json"); - - Message result = function - .apply(MessageBuilder.withPayload("\"bob\"".getBytes(StandardCharsets.UTF_8)).build()); - assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("\"BOB\""); - } - - /* - * Same as above but: - * Given that Java 11 does not include 'javax' packages, this test simply validates that - * the delegation will be made to archive loader where it is available - */ - @Test - public void testWithMainAndStartClassAndSpringConfigurationJavax() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootapp-with-javax/target/bootapp-with-javax-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-name=uppercase" }; - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function, Message> function = catalog.lookup("uppercase", "application/json"); - - Message result = function - .apply(MessageBuilder.withPayload("\"foo@bar.com\"".getBytes(StandardCharsets.UTF_8)).build()); - assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("\"FOO@BAR.COM\""); - } - - /* - * Target function: - * - * @Bean public Function uppercase() - * - * this contains SCF on classpath - */ - @Test - public void testWithMainAndStartClassAndSpringConfigurationAndSCFOnClasspath() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootapp-with-scf/target/bootapp-with-scf-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-name=uppercase" }; - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function, Message> function = catalog.lookup("uppercase", "application/json"); - - Message result = function - .apply(MessageBuilder.withPayload("\"bob\"".getBytes(StandardCharsets.UTF_8)).build()); - assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("\"BOB\""); - } - - /* - * Target function: - * - * @Bean public Function uppercasePerson() - */ - @Test - public void testWithMainAndStartClassAndSpringConfigurationAndTypeConversion() throws Exception { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-name=uppercasePerson" }; - - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - Function, Message> function = catalog.lookup("uppercasePerson", "application/json"); - - Message result = function.apply( - MessageBuilder.withPayload("{\"name\":\"bob\",\"id\":1}".getBytes(StandardCharsets.UTF_8)).build()); - assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("{\"name\":\"BOB\",\"id\":1}"); - } - - /* - * Target Function - * - * @Bean Function, Flux>, Tuple2, Flux>> - */ - @Test - @Disabled - public void testBootAppWithMultipleInputOutput() { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootapp-multi/target/bootapp-multi-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-name=fn" - }; - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - - Function>, Flux>>, Flux>> multiInputFunction = catalog - .lookup("fn", "application/json"); - - Message carEventMessage = MessageBuilder.withPayload("{\"carEvent\":\"CAR IS BUILT\"}".getBytes()).build(); - Message checkoutEventMessage = MessageBuilder.withPayload("{\"checkoutEvent\":\"CAR IS CHECKED OUT\"}".getBytes()).build(); - Flux> carEventStream = Flux.just(carEventMessage); - Flux> checkoutEventStream = Flux.just(checkoutEventMessage); - - Flux> result = multiInputFunction.apply(Tuples.of(carEventStream, checkoutEventStream)); - - byte[] resutBytes = result.blockFirst().getPayload(); - assertThat(resutBytes).isEqualTo("{\"orderEvent\":\"CartEvent: CAR IS BUILT- CheckoutEvent: CAR IS CHECKED OUT\"}".getBytes()); - } - - /* - * Target Function - * - * Function, Flux>, Tuple2, Flux>> - */ - @Test - @Disabled - public void testBootJarWithMultipleInputOutput() { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootjar-multi/target/bootjar-multi-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-class=function.example.Repeater" - }; - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - - Function>, Flux>>, Tuple2>, Flux>>> function = - catalog.lookup("repeater", "application/json", "application/json"); - - Message msg1 = MessageBuilder.withPayload("\"one\"".getBytes()).build(); - Message msg2 = MessageBuilder.withPayload("\"two\"".getBytes()).build(); - Flux> inputOne = Flux.just(msg1, msg2); - - Message msgInt1 = MessageBuilder.withPayload("\"1\"".getBytes()).build(); - Message msgInt2 = MessageBuilder.withPayload("\"2\"".getBytes()).build(); - Flux> inputTwo = Flux.just(msgInt1, msgInt2); - - Tuple2>, Flux>> result = function.apply(Tuples.of(inputOne, inputTwo)); - List result1 = new ArrayList<>(); - List result2 = new ArrayList<>(); - result.getT1().subscribe(message -> { - result1.add(new String(message.getPayload())); - }); - result.getT2().subscribe(message -> { - result2.add(new String(message.getPayload())); - }); - - assertThat(result1.get(0)).isEqualTo("\"one\""); - assertThat(result1.get(1)).isEqualTo("\"two\""); - - assertThat(result2.get(0)).isEqualTo("3"); - assertThat(result2.get(1)).isEqualTo("2"); - } - - // same as previous test, but lookup is empty - @Test - @Disabled - public void testBootJarWithMultipleInputOutputEmptyLookup() { - String[] args = new String[] { - "--spring.cloud.function.location=target/it/bootjar-multi/target/bootjar-multi-1.0.0.RELEASE-exec.jar", - "--spring.cloud.function.function-class=function.example.Repeater" - }; - ApplicationContext context = SpringApplication.run(DeployerApplication.class, args); - FunctionCatalog catalog = context.getBean(FunctionCatalog.class); - - Function>, Flux>>, Tuple2>, Flux>>> function = - catalog.lookup("", "application/json", "application/json"); - - Message msg1 = MessageBuilder.withPayload("\"one\"".getBytes()).build(); - Message msg2 = MessageBuilder.withPayload("\"two\"".getBytes()).build(); - Flux> inputOne = Flux.just(msg1, msg2); - - Message msgInt1 = MessageBuilder.withPayload("\"1\"".getBytes()).build(); - Message msgInt2 = MessageBuilder.withPayload("\"2\"".getBytes()).build(); - Flux> inputTwo = Flux.just(msgInt1, msgInt2); - - Tuple2>, Flux>> result = function.apply(Tuples.of(inputOne, inputTwo)); - List result1 = new ArrayList<>(); - List result2 = new ArrayList<>(); - result.getT1().subscribe(message -> { - result1.add(new String(message.getPayload())); - }); - result.getT2().subscribe(message -> { - result2.add(new String(message.getPayload())); - }); - - assertThat(result1.get(0)).isEqualTo("\"one\""); - assertThat(result1.get(1)).isEqualTo("\"two\""); - - assertThat(result2.get(0)).isEqualTo("3"); - assertThat(result2.get(1)).isEqualTo("2"); - } - - @SpringBootApplication(proxyBeanMethods = false) - private static class DeployerApplication { - @Bean - public MavenProperties mavenProperties() { - MavenProperties properties = new MavenProperties(); - properties.setLocalRepository("mavenrepo/"); - return properties; - } - } -} diff --git a/spring-cloud-function-integration/pom.xml b/spring-cloud-function-integration/pom.xml deleted file mode 100644 index af51f61b6..000000000 --- a/spring-cloud-function-integration/pom.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - 4.0.0 - - spring-cloud-function-integration - Spring Cloud Function with Spring Integration - Spring Cloud Function with Spring Integration - - - spring-cloud-function-parent - org.springframework.cloud - 4.1.0-SNAPSHOT - - - - - - org.springframework.cloud - spring-cloud-function-context - - - org.springframework.boot - spring-boot-starter-integration - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.integration - spring-integration-test - test - - - - diff --git a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowAutoConfiguration.java b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowAutoConfiguration.java deleted file mode 100644 index 03d3848ae..000000000 --- a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowAutoConfiguration.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.integration.dsl; - -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; -import org.springframework.context.annotation.Bean; - -/** - * The auto-configuration to expose a {@link FunctionFlowBuilder} bean - * based on the auto-configured {@link FunctionCatalog}. - * - * @author Artem Bilan - * - * @since 4.0.3 - */ -@AutoConfiguration(after = ContextFunctionCatalogAutoConfiguration.class) -public class FunctionFlowAutoConfiguration { - - @Bean - FunctionFlowBuilder functionFlowBuilder(FunctionCatalog functionCatalog) { - return new FunctionFlowBuilder(functionCatalog); - } - -} diff --git a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowBuilder.java b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowBuilder.java deleted file mode 100644 index 1ad83a440..000000000 --- a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowBuilder.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.integration.dsl; - -import java.util.function.Consumer; -import java.util.function.Supplier; - -import org.reactivestreams.Publisher; - -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.integration.core.MessageSource; -import org.springframework.integration.dsl.GatewayProxySpec; -import org.springframework.integration.dsl.IntegrationFlow; -import org.springframework.integration.dsl.IntegrationFlowBuilder; -import org.springframework.integration.dsl.MessageChannelSpec; -import org.springframework.integration.dsl.MessageProducerSpec; -import org.springframework.integration.dsl.MessageSourceSpec; -import org.springframework.integration.dsl.MessagingGatewaySpec; -import org.springframework.integration.dsl.SourcePollingChannelAdapterSpec; -import org.springframework.integration.endpoint.MessageProducerSupport; -import org.springframework.integration.gateway.MessagingGatewaySupport; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.util.Assert; - -/** - * The entry point for starting a {@link FunctionFlowDefinition}. - * Requires a {@link FunctionCatalog} to lookup function instances - * by their names or definitions from respective operators. - *

- * In addition to standard {@link IntegrationFlow} {@code from()} overloaded methods (for convenience), - * this class introduces {@link #fromSupplier(String)} factory methods to resolve the target {@link Supplier} - * by its name or function definition from the provided {@link FunctionCatalog}. - *

- * This class represents a DSL for functions composition via integration endpoints. - * Extra processing can be done in between functions by the regular {@link IntegrationFlow} operators: - *

- * {@code
- * @Bean
- * IntegrationFlow someFunctionFlow(FunctionFlowBuilder functionFlowBuilder) {
- *		return functionFlowBuilder
- *				.fromSupplier("timeSupplier")
- *				.apply("spelFunction")
- *				.log(LoggingHandler.Level.DEBUG, "some.log.category")
- *				.transform(String::toUpperCase)
- *				.accept("fileConsumer");
- * }
- * }
- * 
- * - * @author Artem Bilan - * - * @since 4.0.3 - */ -public class FunctionFlowBuilder { - - private final FunctionLookupHelper functionLookupHelper; - - public FunctionFlowBuilder(FunctionCatalog functionCatalog) { - Assert.notNull(functionCatalog, "'functionCatalog' must not be null"); - this.functionLookupHelper = new FunctionLookupHelper(functionCatalog); - } - - public FunctionFlowDefinition fromSupplier(String supplierDefinition) { - return fromSupplier(supplierDefinition, null); - } - - public FunctionFlowDefinition fromSupplier(String supplierDefinition, - @Nullable Consumer endpointConfigurer) { - - return fromSupplier(this.functionLookupHelper.lookupSupplier(supplierDefinition), endpointConfigurer); - } - - public FunctionFlowDefinition fromSupplier(Supplier messageSource) { - return fromSupplier(messageSource, null); - } - - public FunctionFlowDefinition fromSupplier(Supplier messageSource, - @Nullable Consumer endpointConfigurer) { - - return toFunctionFlow(IntegrationFlow.fromSupplier(messageSource, endpointConfigurer)); - } - - public FunctionFlowDefinition from(MessageChannel messageChannel) { - return toFunctionFlow(IntegrationFlow.from(messageChannel)); - } - - public FunctionFlowDefinition from(String messageChannelName) { - return from(messageChannelName, false); - } - - public FunctionFlowDefinition from(String messageChannelName, boolean fixedSubscriber) { - return toFunctionFlow(IntegrationFlow.from(messageChannelName, fixedSubscriber)); - } - - public FunctionFlowDefinition from(MessageSourceSpec> messageSourceSpec, - Consumer endpointConfigurer) { - - return toFunctionFlow(IntegrationFlow.from(messageSourceSpec, endpointConfigurer)); - } - - public FunctionFlowDefinition from(MessageSource messageSource) { - return from(messageSource, null); - } - - public FunctionFlowDefinition from(MessageSource messageSource, - @Nullable Consumer endpointConfigurer) { - - return toFunctionFlow(IntegrationFlow.from(messageSource, endpointConfigurer)); - } - - public FunctionFlowDefinition from(MessageProducerSupport messageProducer) { - return toFunctionFlow(IntegrationFlow.from(messageProducer)); - } - - public FunctionFlowDefinition from(MessagingGatewaySupport inboundGateway) { - return toFunctionFlow(IntegrationFlow.from(inboundGateway)); - } - - public FunctionFlowDefinition from(MessageChannelSpec messageChannelSpec) { - return toFunctionFlow(IntegrationFlow.from(messageChannelSpec)); - } - - public FunctionFlowDefinition from(MessageProducerSpec messageProducerSpec) { - return toFunctionFlow(IntegrationFlow.from(messageProducerSpec)); - } - - public FunctionFlowDefinition from(MessageSourceSpec> messageSourceSpec) { - return toFunctionFlow(IntegrationFlow.from(messageSourceSpec)); - } - - public FunctionFlowDefinition from(MessagingGatewaySpec inboundGatewaySpec) { - return toFunctionFlow(IntegrationFlow.from(inboundGatewaySpec)); - } - - public FunctionFlowDefinition from(Class serviceInterface) { - return from(serviceInterface, null); - } - - public FunctionFlowDefinition from(Class serviceInterface, - @Nullable Consumer endpointConfigurer) { - - return toFunctionFlow(IntegrationFlow.from(serviceInterface, endpointConfigurer)); - } - - public FunctionFlowDefinition from(Publisher> publisher) { - return toFunctionFlow(IntegrationFlow.from(publisher)); - } - - private FunctionFlowDefinition toFunctionFlow(IntegrationFlowBuilder from) { - FunctionFlowDefinition functionFlow = new FunctionFlowDefinition(this.functionLookupHelper); - from.channel(functionFlow.getInputChannel()); - functionFlow.addUpstreamComponents(from.get().getIntegrationComponents()); - return functionFlow; - } - -} diff --git a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowDefinition.java b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowDefinition.java deleted file mode 100644 index cc8b84553..000000000 --- a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionFlowDefinition.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.integration.dsl; - -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.springframework.integration.dsl.IntegrationFlow; -import org.springframework.integration.dsl.IntegrationFlowExtension; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; - -/** - * The {@link IntegrationFlowExtension} implementation for Spring Cloud Function domain. - * Adds operators for functions and consumers and overloaded versions based on their names - * or definitions resolved from the provided {@link org.springframework.cloud.function.context.FunctionCatalog}. - * - * @author Artem Bilan - * - * @since 4.0.3 - */ -public final class FunctionFlowDefinition extends IntegrationFlowExtension { - - private final FunctionLookupHelper functionLookupHelper; - - FunctionFlowDefinition(FunctionLookupHelper functionLookupHelper) { - this.functionLookupHelper = functionLookupHelper; - } - - MessageChannel getInputChannel() { - return getCurrentMessageChannel(); - } - - void addUpstreamComponents(Map components) { - addComponents(components); - } - - /** - * Configure a {@link org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper} - * as a handler in the endpoint by its definition from the - * {@link org.springframework.cloud.function.context.FunctionCatalog}. - * @param functionDefinition the function definition in the function catalog. - * @return the current flow builder. - */ - public FunctionFlowDefinition apply(String functionDefinition) { - return apply(this.functionLookupHelper.lookupFunction(functionDefinition)); - } - - /** - * Configure a {@link Function} as a handler in the endpoint. - * @param function the {@link Function} to use. - * @return the current flow builder. - */ - public FunctionFlowDefinition apply(Function, ?> function) { - return handle(Message.class, (message, headers) -> function.apply(message)); - } - - /** - * Configure a {@link org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper} - * as a one-way handler in the final endpoint by its definition from the - * {@link org.springframework.cloud.function.context.FunctionCatalog}. - * @param consumerDefinition the consumer definition in the function catalog. - * @return the current flow builder. - */ - public IntegrationFlow accept(String consumerDefinition) { - return accept(this.functionLookupHelper.lookupConsumer(consumerDefinition)); - } - - /** - * Configure a {@link Consumer} as a one-way handler in the final endpoint. - * @param consumer the {@link Consumer} to use. - * @return the current flow builder. - */ - public IntegrationFlow accept(Consumer> consumer) { - return handle(consumer::accept) - .get(); - } - -} diff --git a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionLookupHelper.java b/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionLookupHelper.java deleted file mode 100644 index 262ee1f5e..000000000 --- a/spring-cloud-function-integration/src/main/java/org/springframework/cloud/function/integration/dsl/FunctionLookupHelper.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.integration.dsl; - -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.util.Assert; - -/** - * The helper class to lookup functions from the catalog in lazy manner and cache their instances. - * - * @author Artem Bilan - * - * @since 4.0.3 - */ -public class FunctionLookupHelper { - - private final FunctionCatalog functionCatalog; - - FunctionLookupHelper(FunctionCatalog functionCatalog) { - this.functionCatalog = functionCatalog; - } - -

Supplier

lookupSupplier(String functionDefinition) { - Supplier> memoizedSupplier = lazyLookup(Supplier.class, functionDefinition); - return () -> memoizedSupplier.get().get(); - } - -

Function lookupFunction(String functionDefinition) { - Supplier> memoizedFunction = lazyLookup(Function.class, functionDefinition); - return (p) -> memoizedFunction.get().apply(p); - } - -

Consumer

lookupConsumer(String consumerDefinition) { - Supplier> memoizedConsumer = lazyLookup(Consumer.class, consumerDefinition); - return (p) -> memoizedConsumer.get().accept(p); - } - - private Supplier lazyLookup(Class functionType, String functionDefinition) { - return memoize(() -> requireNonNull(functionType, functionDefinition)); - } - - private T requireNonNull(Class functionType, String functionDefinition) { - T function = this.functionCatalog.lookup(functionType, functionDefinition); - Assert.notNull(function, () -> "No '" + functionDefinition + "' in the catalog"); - return function; - } - - /** - * The delegate {@link Supplier#get()} is called exactly once and the result is cached. - * @param Generic type of supplied value - * @param delegate The actual Supplier - * @return The memoized Supplier - */ - private static Supplier memoize(Supplier delegate) { - AtomicReference value = new AtomicReference<>(); - return () -> { - T val = value.get(); - if (val == null) { - synchronized (value) { - val = value.get(); - if (val == null) { - val = delegate.get(); - value.set(val); - } - } - } - return val; - }; - } - -} diff --git a/spring-cloud-function-integration/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-function-integration/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index b7d39cea2..000000000 --- a/spring-cloud-function-integration/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -org.springframework.cloud.function.integration.dsl.FunctionFlowAutoConfiguration diff --git a/spring-cloud-function-integration/src/test/java/org/springframework/cloud/function/integration/dsl/FunctionFlowTests.java b/spring-cloud-function-integration/src/test/java/org/springframework/cloud/function/integration/dsl/FunctionFlowTests.java deleted file mode 100644 index e4ee38a2b..000000000 --- a/spring-cloud-function-integration/src/test/java/org/springframework/cloud/function/integration/dsl/FunctionFlowTests.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.integration.dsl; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.integration.channel.QueueChannel; -import org.springframework.integration.dsl.IntegrationFlow; -import org.springframework.integration.dsl.PollerSpec; -import org.springframework.integration.dsl.Pollers; -import org.springframework.integration.dsl.context.IntegrationFlowContext; -import org.springframework.integration.endpoint.SourcePollingChannelAdapter; -import org.springframework.integration.handler.LoggingHandler; -import org.springframework.integration.scheduling.PollerMetadata; -import org.springframework.integration.test.util.OnlyOnceTrigger; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageDeliveryException; -import org.springframework.messaging.support.GenericMessage; -import org.springframework.test.annotation.DirtiesContext; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * @author Artem Bilan - * - * @since 4.0.3 - */ -@SpringBootTest -@DirtiesContext -public class FunctionFlowTests { - - // To verify cached lookups - @SpyBean - FunctionCatalog functionCatalog; - - @Autowired - BlockingQueue results; - - @Test - void fromSupplierOverFunctionToConsumer(@Autowired SourcePollingChannelAdapter supplierEndpoint, - @Autowired QueueChannel wireTapChannel) throws InterruptedException { - - supplierEndpoint.start(); - - String result = this.results.poll(10, TimeUnit.SECONDS); - assertThat(result).isEqualTo("SIMPLE TEST DATA"); - Message receive = wireTapChannel.receive(10_000); - assertThat(receive) - .extracting(Message::getPayload) - .isEqualTo("simple test data".getBytes()); - - supplierEndpoint.stop(); - } - - @Test - void fromChannelToFunctionComposition(@Autowired MessageChannel functionCompositionInput) - throws InterruptedException { - - functionCompositionInput.send(new GenericMessage<>("compose this")); - - String result = this.results.poll(10, TimeUnit.SECONDS); - assertThat(result).isEqualTo("COMPOSE THIS"); - - functionCompositionInput.send(new GenericMessage<>("compose again")); - - result = this.results.poll(10, TimeUnit.SECONDS); - assertThat(result).isEqualTo("COMPOSE AGAIN"); - - // Ensure that FunctionLookupHelper.memoize() does its trick calling FunctionCatalog.lookup() only once - verify(this.functionCatalog).lookup(Consumer.class, "upperCaseFunction|simpleStringConsumer"); - } - - @Test - void noFunctionInCatalogException(@Autowired IntegrationFlowContext integrationFlowContext) { - // We need to mock here since BeanFactoryAwareFunctionRegistry will have slightly different logic - FunctionCatalog mockFunctionCatalog = mock(FunctionCatalog.class); - - FunctionFlowBuilder functionFlowBuilder = new FunctionFlowBuilder(mockFunctionCatalog); - - IntegrationFlow wrongFlow = - functionFlowBuilder.from("inputChannel") - .accept("nonExistingConsumer"); - - IntegrationFlowContext.IntegrationFlowRegistration registration = - integrationFlowContext.registration(wrongFlow) - .register(); - - assertThatExceptionOfType(MessageDeliveryException.class) - .isThrownBy(() -> registration.getInputChannel().send(new GenericMessage<>("test"))) - .withRootCauseInstanceOf(IllegalArgumentException.class) - .withStackTraceContaining("No 'nonExistingConsumer' in the catalog"); - - registration.destroy(); - } - - - @EnableAutoConfiguration - @Configuration(proxyBeanMethods = false) - static class TestIntegrationConfiguration { - - @Bean(PollerMetadata.DEFAULT_POLLER) - PollerSpec defaultPoller() { - return Pollers.trigger(new OnlyOnceTrigger()); - } - - @Bean - Supplier simpleByteArraySupplier() { - return "simple test data"::getBytes; - } - - @Bean - Function upperCaseFunction() { - return String::toUpperCase; - } - - @Bean - BlockingQueue results() { - return new LinkedBlockingQueue<>(); - } - - @Bean - Consumer simpleStringConsumer(BlockingQueue results) { - return results::add; - } - - @Bean - QueueChannel wireTapChannel() { - return new QueueChannel(); - } - - @Bean - IntegrationFlow someFunctionFlow(FunctionFlowBuilder functionFlowBuilder) { - return functionFlowBuilder - .fromSupplier("simpleByteArraySupplier", e -> e.id("supplierEndpoint").autoStartup(false)) - .wireTap("wireTapChannel") - .apply("upperCaseFunction") - .log(LoggingHandler.Level.WARN, FunctionFlowTests.class.getName()) - .accept("simpleStringConsumer"); - } - - @Bean - IntegrationFlow functionCompositionFlow(FunctionFlowBuilder functionFlowBuilder) { - return functionFlowBuilder - .from("functionCompositionInput") - .accept("upperCaseFunction|simpleStringConsumer"); - } - - } - -} diff --git a/spring-cloud-function-kotlin/.jdk8 b/spring-cloud-function-kotlin/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-kotlin/README.adoc b/spring-cloud-function-kotlin/README.adoc deleted file mode 100644 index e76a1c926..000000000 --- a/spring-cloud-function-kotlin/README.adoc +++ /dev/null @@ -1,4 +0,0 @@ -!!! INTERNAL !!! - -Contains only Kotlin tests. -Since version 3.1.3, user's should not be declaring explicit dependency on this module. \ No newline at end of file diff --git a/spring-cloud-function-kotlin/pom.xml b/spring-cloud-function-kotlin/pom.xml deleted file mode 100644 index 1f42e42a4..000000000 --- a/spring-cloud-function-kotlin/pom.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-kotlin - jar - Spring Cloud Function Kotlin Support - Spring Cloud Function Kotlin Support - - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - - - - org.springframework.cloud - spring-cloud-function-context - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - - com.amazonaws - aws-lambda-java-events - 3.9.0 - provided - - - com.amazonaws - aws-lambda-java-core - 1.2.1 - provided - - - com.amazonaws - aws-lambda-java-serialization - 1.0.0 - provided - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - - - org.jetbrains.kotlin - kotlin-reflect - - - org.jetbrains.kotlinx - kotlinx-coroutines-reactor - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.cloud - spring-cloud-function-web - test - - - org.springframework - spring-webflux - test - - - org.springframework.boot - spring-boot-starter-web - test - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - - - - kotlin-maven-plugin - org.jetbrains.kotlin - - - test-compile - - test-compile - - - - ${project.basedir}/src/test/kotlin - ${project.basedir}/src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - default-compile - none - - - - default-testCompile - none - - - java-compile - compile - - compile - - - - java-test-compile - test-compile - - testCompile - - - - - - - - diff --git a/spring-cloud-function-kotlin/src/main/resources/META-INF/spring.factories b/spring-cloud-function-kotlin/src/main/resources/META-INF/spring.factories deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinSuspendTests.java b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinSuspendTests.java deleted file mode 100644 index 874de12a4..000000000 --- a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinSuspendTests.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.kotlin; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; - -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.context.support.GenericApplicationContext; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Adrien Poupard - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class ContextFunctionCatalogAutoConfigurationKotlinSuspendTests { - - private GenericApplicationContext context; - - private FunctionCatalog catalog; - - @AfterEach - public void close() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void typeDiscoveryTests() { - create(new Class[] { KotlinSuspendFlowLambdasConfiguration.class, - ContextFunctionCatalogAutoConfigurationKotlinTests.SimpleConfiguration.class }); - - FunctionCatalog functionCatalog = this.context.getBean(FunctionCatalog.class); - - FunctionInvocationWrapper kotlinFunction = functionCatalog.lookup("kotlinFunction"); - assertThat(kotlinFunction.isFunction()).isTrue(); - assertThat(kotlinFunction.getInputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); - assertThat(kotlinFunction.getOutputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); - - FunctionInvocationWrapper kotlinConsumer = functionCatalog.lookup("kotlinConsumer"); - assertThat(kotlinConsumer.isConsumer()).isTrue(); - assertThat(kotlinConsumer.getInputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); - - FunctionInvocationWrapper kotlinSupplier = functionCatalog.lookup("kotlinSupplier"); - assertThat(kotlinSupplier.isSupplier()).isTrue(); - assertThat(kotlinSupplier.getOutputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); - - FunctionInvocationWrapper kotlinPojoFunction = functionCatalog.lookup("kotlinPojoFunction"); - assertThat(kotlinPojoFunction.isFunction()).isTrue(); - assertThat(kotlinPojoFunction.getInputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); - assertThat(kotlinPojoFunction.getOutputType().getTypeName()).isEqualTo("reactor.core.publisher.Flux"); - } - - private void create(Class[] types, String... props) { - this.context = (GenericApplicationContext) new SpringApplicationBuilder(types).properties(props).run(); - this.catalog = this.context.getBean(FunctionCatalog.class); - } -} diff --git a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java deleted file mode 100644 index 5954bf4c8..000000000 --- a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.kotlin; - -import java.util.function.Function; -import java.util.function.Supplier; - -import kotlin.jvm.functions.Function0; -import kotlin.jvm.functions.Function1; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.messaging.support.MessageBuilder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Oleg Zhurakousky - */ -public class ContextFunctionCatalogAutoConfigurationKotlinTests { - - private GenericApplicationContext context; - - private FunctionCatalog catalog; - - @AfterEach - public void close() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void typeDiscoveryTests() { - create(new Class[] { KotlinLambdasConfiguration.class, - SimpleConfiguration.class, - KotlinComponentFunction.class, - ComponentUppercase.class}); - - FunctionCatalog functionCatalog = this.context.getBean(FunctionCatalog.class); - - FunctionInvocationWrapper kotlinComponentFunction = functionCatalog.lookup("kotlinComponentFunction"); - assertThat(kotlinComponentFunction.isFunction()).isTrue(); - assertThat(kotlinComponentFunction.getInputType().getTypeName()).isEqualTo("java.lang.String"); - assertThat(kotlinComponentFunction.getOutputType().getTypeName()).isEqualTo("java.lang.String"); - assertThat(kotlinComponentFunction.apply("bob")).isEqualTo("BOB"); - - FunctionInvocationWrapper kotlinFunction = functionCatalog.lookup("kotlinFunction"); - assertThat(kotlinFunction.isFunction()).isTrue(); - assertThat(kotlinFunction.getInputType()).isEqualTo(String.class); - assertThat(kotlinFunction.getOutputType()).isEqualTo(String.class); - - FunctionInvocationWrapper kotlinConsumer = functionCatalog.lookup("kotlinConsumer"); - assertThat(kotlinConsumer.isConsumer()).isTrue(); - assertThat(kotlinConsumer.getInputType()).isEqualTo(String.class); - - FunctionInvocationWrapper kotlinSupplier = functionCatalog.lookup("kotlinSupplier"); - assertThat(kotlinSupplier.isSupplier()).isTrue(); - assertThat(kotlinSupplier.getOutputType()).isEqualTo(String.class); - - FunctionInvocationWrapper kotlinPojoFunction = functionCatalog.lookup("kotlinPojoFunction"); - assertThat(kotlinPojoFunction.isFunction()).isTrue(); - assertThat(kotlinPojoFunction.getInputType()).isEqualTo(Person.class); - assertThat(kotlinPojoFunction.getOutputType()).isEqualTo(String.class); - - FunctionInvocationWrapper kotlinListPojoFunction = functionCatalog.lookup("kotlinListPojoFunction"); - assertThat(kotlinListPojoFunction.isFunction()).isTrue(); - assertThat(kotlinListPojoFunction.getInputType().getTypeName()).isEqualTo("java.util.List"); - assertThat(kotlinListPojoFunction.getOutputType()).isEqualTo(String.class); - - FunctionInvocationWrapper componentUppercase = functionCatalog.lookup("componentUppercase"); - assertThat(componentUppercase.isFunction()).isTrue(); - assertThat(componentUppercase.getInputType()).isEqualTo(String.class); - assertThat(componentUppercase.getOutputType()).isEqualTo(String.class); - - assertThat(componentUppercase.apply("hello")).isEqualTo("HELLO"); - - FunctionInvocationWrapper uppercaseBean = functionCatalog.lookup("uppercase"); - assertThat(uppercaseBean.isFunction()).isTrue(); - assertThat(uppercaseBean.getInputType()).isEqualTo(String.class); - assertThat(uppercaseBean.getOutputType()).isEqualTo(String.class); - - assertThat(uppercaseBean.apply("hello")).isEqualTo("HELLO"); - } - - @Test - public void testWithComplexTypesAndRouting() { - create(new Class[] { KotlinLambdasConfiguration.class, - SimpleConfiguration.class }); - - FunctionInvocationWrapper function = this.catalog.lookup("kotlinListPojoFunction"); - String result = (String) function.apply("[{\"name\":\"Ricky\"}]"); - assertThat(result).isEqualTo("List of: Ricky"); - - function = this.catalog.lookup(Function.class, "functionRouter"); - result = (String) function.apply(MessageBuilder.withPayload("[{\"name\":\"Ricky\"}]") - .setHeader("spring.cloud.function.definition", "kotlinListPojoFunction").build()); - assertThat(result).isEqualTo("List of: Ricky"); - - } - - @Test - public void kotlinLambdas() { - create(new Class[] { KotlinLambdasConfiguration.class, - SimpleConfiguration.class }); - - assertThat(this.context.getBean("kotlinFunction")).isInstanceOf(Function1.class); - FunctionInvocationWrapper function = this.catalog.lookup(Function.class, "kotlinFunction"); - assertThat(function).isInstanceOf(Function.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(function.getInputType()))).isAssignableFrom(String.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(function.getOutputType()))).isAssignableFrom(String.class); - - - function = this.catalog.lookup(Function.class, "kotlinConsumer"); - assertThat(this.context.getBean("kotlinConsumer")).isInstanceOf(Function1.class); - assertThat(function).isInstanceOf(Function.class); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(function.getInputType()))).isAssignableFrom(String.class); - - - assertThat(this.context.getBean("kotlinSupplier")).isInstanceOf(Function0.class); - FunctionInvocationWrapper supplier = this.catalog.lookup(Function.class, "kotlinSupplier"); - assertThat(supplier).isInstanceOf(Supplier.class); - assertThat(supplier.get()).isEqualTo("Hello"); - assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(supplier.getOutputType()))).isAssignableFrom(String.class); - - function = this.catalog.lookup(Function.class, "kotlinFunction|function2"); - assertThat(function.apply("Hello")).isEqualTo("HELLOfunction2"); - - Function javaFunction = this.catalog - .lookup(Function.class, "javaFunction"); - assertThat(javaFunction.apply("Hello")) - .isEqualTo("Hello"); - } - - private void create(Class[] types, String... props) { - this.context = (GenericApplicationContext) new SpringApplicationBuilder(types).properties(props).run(); - this.catalog = this.context.getBean(FunctionCatalog.class); - } - - @EnableAutoConfiguration - @Configuration - protected static class SimpleConfiguration { - - @Bean - public Function function2() { - return value -> value + "function2"; - } - - } - -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/ComponentUppercase.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/ComponentUppercase.kt deleted file mode 100644 index 574b6aa48..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/ComponentUppercase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.springframework.cloud.function.kotlin - -import org.springframework.stereotype.Component - -@Component -class ComponentUppercase : (String) -> String { - override fun invoke(p1: String): String { - return p1.uppercase() - } -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinComponentFunction.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinComponentFunction.kt deleted file mode 100644 index 629f981ff..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinComponentFunction.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.springframework.cloud.function.kotlin - -import org.springframework.stereotype.Component -import java.util.function.Function - -@Component -class KotlinComponentFunction : Function { - - override fun apply(t: String): String { - return t.uppercase(); - } -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinLambdasConfiguration.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinLambdasConfiguration.kt deleted file mode 100644 index a324131fc..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinLambdasConfiguration.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.kotlin - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import java.util.function.Function -import java.util.List - -/** - * @author Oleg Zhurakousky - * - */ -@EnableAutoConfiguration -@Configuration -open class KotlinLambdasConfiguration { - - @Bean - open fun uppercase(): Function = KotlinComponentFunction() - @Bean - open fun kotlinFunction(): (String) -> String { - return { it.toUpperCase() } - } - - @Bean - open fun kotlinPojoFunction(): (Person) -> String { - return { it.name.toString()} - } - - @Bean - open fun kotlinListPojoFunction(): (List) -> String { - return { - "List of: " + it.get(0).name - } - } - - @Bean - open fun kotlinConsumer(): (String) -> Unit { - return { println(it) } - } - - @Bean - open fun kotlinSupplier(): () -> String { - return { "Hello" } - } - - @Bean - open fun javaFunction(): Function { - return Function { x -> x } - } - -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinSuspendFlowLambdasConfiguration.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinSuspendFlowLambdasConfiguration.kt deleted file mode 100644 index bfa7e3102..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinSuspendFlowLambdasConfiguration.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.kotlin - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.map -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import reactor.core.publisher.Flux -import java.util.function.Function - -/** - * @author Adrien Poupard - * - */ -@EnableAutoConfiguration -@Configuration -open class KotlinSuspendFlowLambdasConfiguration { - - @Bean - open fun kotlinFunction(): suspend (Flow) -> Flow = { flow -> - flow.map { value -> value.toUpperCase() } - } - - @Bean - open fun kotlinPojoFunction(): suspend (Flow) -> Flow = { flow -> - flow.map(Person::toString) - } - - @Bean - open fun kotlinConsumer(): suspend (Flow) -> Unit = { flow -> - flow.collect(::println) - } - - @Bean - open fun kotlinSupplier(): suspend () -> Flow = { - flow { - emit("Hello") - } - } - - @Bean - open fun javaFunction(): Function, Flux> { - return Function { x -> x } - } - -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinSuspendLambdasConfiguration.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinSuspendLambdasConfiguration.kt deleted file mode 100644 index 68e985550..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinSuspendLambdasConfiguration.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.kotlin - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration - -/** - * @author Adrien Poupard - * - */ -@EnableAutoConfiguration -@Configuration -open class KotlinSuspendLambdasConfiguration { - - @Bean - open fun kotlinFunction(): suspend (Person) -> String { - return { it.name.toString()} - } - - @Bean - open fun kotlinConsumer(): suspend (String) -> Unit { - return { println(it) } - } - - @Bean - open fun kotlinSupplier(): suspend () -> String { - return { "Hello" } - } - -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/Person.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/Person.kt deleted file mode 100644 index f46fb3278..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/Person.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.kotlin - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import java.util.function.Function - -/** - * @author Oleg Zhurakousky - * - */ -class Person { - - var name:String? = null; - - -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/aws/AwsKotlinTestsTests.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/aws/AwsKotlinTestsTests.kt deleted file mode 100644 index 36eafbbe3..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/aws/AwsKotlinTestsTests.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.function.kotlin.aws - -import org.junit.jupiter.api.Test -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.cloud.function.adapter.aws.FunctionInvoker -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream - -/** - * @author Oleg Zhurakousky - */ -//@SpringBootTest() -//@ContextConfiguration(classes = [RestApplication::class, AwsKotlinTestsTests.TestConfiguration::class]) -open class AwsKotlinTestsTests { - var dynamoDbEvent:String = "{\n" + - " \"Records\": [\n" + - " {\n" + - " \"eventID\": \"dc1e145db718184b1c809f989335b168\",\n" + - " \"eventName\": \"INSERT\",\n" + - " \"eventVersion\": \"1.1\",\n" + - " \"eventSource\": \"aws:dynamodb\",\n" + - " \"awsRegion\": \"eu-central-1\",\n" + - " \"dynamodb\": {\n" + - " \"ApproximateCreationDateTime\": 1.689335433E9,\n" + - " \"Keys\": {\n" + - " \"version\": {\n" + - " \"N\": \"1\"\n" + - " },\n" + - " \"urlPath\": {\n" + - " \"S\": \"image/6037/2023/07/14/1d058d91-c9db-4c6a-aadf-4ab749de95d1.jpg\"\n" + - " }\n" + - " },\n" + - " \"NewImage\": {\n" + - " \"createdAt\": {\n" + - " \"N\": \"1689335427\"\n" + - " },\n" + - " \"provider\": {\n" + - " \"S\": \"XXXXXX\"\n" + - " },\n" + - " \"urlPath\": {\n" + - " \"S\": \"image/6037/2023/07/14/1d058d91-c9db-4c6a-aadf-4ab749de95d1.jpg\"\n" + - " },\n" + - " \"version\": {\n" + - " \"N\": \"1\"\n" + - " },\n" + - " \"status\": {\n" + - " \"S\": \"SUCCESS\"\n" + - " }\n" + - " },\n" + - " \"SequenceNumber\": \"1049234200000000032682603273\",\n" + - " \"SizeBytes\": 7869,\n" + - " \"StreamViewType\": \"NEW_IMAGE\"\n" + - " },\n" + - " \"eventSourceARN\": \"arn:aws:dynamodb:eu-central-1:xxxxxxx:table/example-results/stream/2022-12-06T16:23:45.860\"\n" + - " }\n" + - " ]\n" + - "}" - - @Test - open fun testDynamoDb() { - System.setProperty("MAIN_CLASS", KotlinAwsLambdasConfiguration::class.java.getName()) - System.setProperty("spring.cloud.function.definition", "handleDynamoDbEvent") - var invoker = FunctionInvoker() - - var targetStream = ByteArrayInputStream(this.dynamoDbEvent.toByteArray()) - var output = ByteArrayOutputStream() - invoker.handleRequest(targetStream, output, null) - } -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/aws/KotlinAwsLambdasConfiguration.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/aws/KotlinAwsLambdasConfiguration.kt deleted file mode 100644 index 315c1b63a..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/aws/KotlinAwsLambdasConfiguration.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.kotlin.aws - -import com.amazonaws.services.lambda.runtime.events.DynamodbEvent -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.map -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.messaging.Message -import reactor.core.publisher.Flux -import java.util.function.Function - -/** - * @author Adrien Poupard - * - */ -@EnableAutoConfiguration -@Configuration -open class KotlinAwsLambdasConfiguration { - - @Bean - open fun handleDynamoDbEvent(): (Message) -> Unit { - return { println(it) } - } -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageSuspendTests.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageSuspendTests.kt deleted file mode 100644 index 85ad7f68f..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageSuspendTests.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.function.kotlin.web - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment -import org.springframework.boot.test.web.client.TestRestTemplate -import org.springframework.cloud.function.web.RestApplication -import org.springframework.context.annotation.Bean -import org.springframework.http.MediaType -import org.springframework.http.RequestEntity -import org.springframework.messaging.Message -import org.springframework.messaging.support.MessageBuilder -import org.springframework.test.context.ContextConfiguration -import java.net.URI - -/** - * @author Adrien Poupard - */ -@SpringBootTest( - webEnvironment = WebEnvironment.RANDOM_PORT, - properties = ["spring.cloud.function.web.path=/functions", "spring.main.web-application-type=reactive"] -) -@ContextConfiguration(classes = [RestApplication::class, HeadersToMessageSuspendTests.TestConfiguration::class]) -open class HeadersToMessageSuspendTests { - @Autowired - private val rest: TestRestTemplate? = null - @Test - @Throws(Exception::class) - open fun testBodyAndCustomHeaderFromMessagePropagation() { - // test POJO paylod - var postForEntity = rest!! - .exchange( - RequestEntity.post(URI("/functions/employeeSuspend")) - .contentType(MediaType.APPLICATION_JSON) - .body("[{\"name\":\"Bob\",\"age\":25}]"), String::class.java - ) - - Assertions.assertThat(postForEntity.body).isEqualTo("[{\"name\":\"Bob\",\"age\":25}]") - Assertions.assertThat(postForEntity.headers.containsKey("x-content-type")).isTrue - Assertions.assertThat(postForEntity.headers["x-content-type"]!![0]) - .isEqualTo("application/xml") - Assertions.assertThat(postForEntity.headers["foo"]!![0]).isEqualTo("bar") - - // test simple type payload - postForEntity = rest.postForEntity( - URI("/functions/stringSuspend"), - "HELLO", String::class.java - ) - Assertions.assertThat(postForEntity.body).isEqualTo("[\"HELLO\"]") - Assertions.assertThat(postForEntity.headers.containsKey("x-content-type")).isTrue - Assertions.assertThat(postForEntity.headers["x-content-type"]!![0]) - .isEqualTo("application/xml") - Assertions.assertThat(postForEntity.headers["foo"]!![0]).isEqualTo("bar") - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - open class TestConfiguration { - @Bean("stringSuspend") - open fun functiono():suspend (employee: Flow>) -> Flow> = { flow: Flow> -> - flow.map { request -> - val message = - MessageBuilder.withPayload(request.payload) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build() - message - } - } - - @Bean("employeeSuspend") - open fun function1(): suspend (employee: Flow>) -> Flow> = { flow -> - flow.map { request -> - val message = - MessageBuilder - .withPayload(request.payload) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar") - .build() - message - } - } - } - - open class Employee { - var name: String? = null - var age = 0 - } -} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageTests.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageTests.kt deleted file mode 100644 index 02f1c170d..000000000 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageTests.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.function.kotlin.web - -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment -import org.springframework.boot.test.web.client.TestRestTemplate -import org.springframework.cloud.function.web.RestApplication -import org.springframework.context.annotation.Bean -import org.springframework.http.MediaType -import org.springframework.http.RequestEntity -import org.springframework.messaging.Message -import org.springframework.messaging.support.MessageBuilder -import org.springframework.test.context.ContextConfiguration -import java.lang.Exception -import java.net.URI - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -@SpringBootTest( - webEnvironment = WebEnvironment.RANDOM_PORT, - properties = ["spring.cloud.function.web.path=/functions", "spring.main.web-application-type=reactive"] -) -@ContextConfiguration(classes = [RestApplication::class, HeadersToMessageTests.TestConfiguration::class]) -open class HeadersToMessageTests { - @Autowired - private val rest: TestRestTemplate? = null - @Test - @Throws(Exception::class) - open fun testBodyAndCustomHeaderFromMessagePropagation() { - // test POJO paylod - var postForEntity = rest!! - .exchange( - RequestEntity.post(URI("/functions/employee")) - .contentType(MediaType.APPLICATION_JSON) - .body("{\"name\":\"Bob\",\"age\":25}"), String::class.java - ) - Assertions.assertThat(postForEntity.body).isEqualTo("{\"name\":\"Bob\",\"age\":25}") - Assertions.assertThat(postForEntity.headers.containsKey("x-content-type")).isTrue - Assertions.assertThat(postForEntity.headers["x-content-type"]!![0]) - .isEqualTo("application/xml") - Assertions.assertThat(postForEntity.headers["foo"]!![0]).isEqualTo("bar") - - // test simple type payload - postForEntity = rest.postForEntity( - URI("/functions/string"), - "{\"name\":\"Bob\",\"age\":25}", String::class.java - ) - Assertions.assertThat(postForEntity.body).isEqualTo("{\"name\":\"Bob\",\"age\":25}") - Assertions.assertThat(postForEntity.headers.containsKey("x-content-type")).isTrue - Assertions.assertThat(postForEntity.headers["x-content-type"]!![0]) - .isEqualTo("application/xml") - Assertions.assertThat(postForEntity.headers["foo"]!![0]).isEqualTo("bar") - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - open class TestConfiguration { - @Bean("string") - open fun functiono(): (message: Message) -> Message = { request: Message -> - val message = - MessageBuilder.withPayload(request.payload) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build() - message - } - - @Bean("employee") - open fun function1(): (employee: Message) -> Message = { request -> - val message = - MessageBuilder - .withPayload(request.payload) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build() - message - } - } - - // used by json converter - open class Employee { - var name: String? = null - var age = 0 - } -} diff --git a/spring-cloud-function-rsocket/.jdk8 b/spring-cloud-function-rsocket/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-rsocket/NOTES.txt b/spring-cloud-function-rsocket/NOTES.txt deleted file mode 100644 index 60f6788f7..000000000 --- a/spring-cloud-function-rsocket/NOTES.txt +++ /dev/null @@ -1,7 +0,0 @@ - -spring.cloud.function.rsocket.bind-address=localhost -spring.cloud.function.rsocket.bind-port=1234 - -spring.cloud.function.rsocket.target-address=localhost -spring.cloud.function.rsocket.target-port=1235 - diff --git a/spring-cloud-function-rsocket/README.md b/spring-cloud-function-rsocket/README.md deleted file mode 100644 index aad21631b..000000000 --- a/spring-cloud-function-rsocket/README.md +++ /dev/null @@ -1,99 +0,0 @@ -### Introduction - -Spring Cloud Function allows you to invoke function via [RSocket](https://rsocket.io/). While you can read more about RSocket and it’s java -implementation [here](https://github.com/rsocket/rsocket-java), this section will describe the parts relevant to Spring Cloud Function integration. - -### Programming model -From the user perspective bringing RSocket does not change the implementation of functions or any of its features, such as type conversion, -composition, POJO functions etc. -And while RSocket allows first class reactive interaction over the network supporting important reactive features such as back pressure, -users of Spring Cloud Function still have freedom to implement their business logic using reactive or imperative functions delegating any -adjustment needed to apply proper invocation model to the framework. - -To use RSocket integration all you need is to add `spring-cloud-function-rsocket` dependency to your classpath -``` - - org.springframework.cloud - spring-cloud-function-rsocket - -``` - -To interact with functions via RSocket we rely on Spring Boot support for RSocket and `RSocketRequester.Builder` API. -The code below shows the key parts and you can get more details on various interaction models -from [this test case](https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationTests.java). - - -``` -@Bean -public Function uppercase() { - return v -> v.toUpperCase(); -} - -. . . - -RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - -rsocketRequesterBuilder.tcp("localhost", port) - .route(“uppercase") - .data("\"hello\"") - .retrieveMono(String.class) - .subscribe(System.out::println); -``` - -Once connected to RSocket we use `route` operation to specify which function we want to invoke providing the actual -payload via `data` operation. Then we use one of the `retrieve` operations that best suits our desired interaction -(RSocket supports multiple interaction models such as fire-and-forget, request-reply etc.) - -#### Order of priority for routing instructions - -As you can see from the preceding examples, we provide function definition as a value to `route(..)` operator of `RSocketRequester.Builder`. -However that is not the only way. You can also use standard `spring.cloud.function.definition` property as well as `spring.cloud.function.routing-expression` or property or `MessageRoutingCallback` on the server side of the RSocket interaction (see "Function Routing and Filtering" section of reference manual). -This raises a question of _order_ and _priorities_ when it comes to reconsiling a conflict in the event several ways of providing definition are used. So it is a mater of clearly stating the rule which is: - -***1 - MessageRoutingCallback*** -The `MessageRoutingCallback` takes precedence over all other ways of providing function definition resolution. - -***2 - spring.cloud.function.routing-expression*** -The `spring.cloud.function.routing-expression` property takes next precedence. So, in the event you may have also use `route(..)` operator or `spring.cloud.function.definition` property, they will be ignored if `spring.cloud.function.routing-expression` property is provided. - -***3 - route(..)*** -The next in line is `route(..)` operator. So in the event there are no `spring.cloud.function.routing-expression` property but you defined `spring.cloud.function.definition` property, it will be ignored in favor of definition provided by the `route(..)` operator. - -***4 - spring.cloud.function.definition*** -The `spring.cloud.function.definition` property is the last in the list allowing you to simply `route("")` to empty string. - - -### Messaging - -If you want to provide and/or receive additional information that you would normally communicate via Message headers you can send and receive Spring `Message`. -For example, the following tests case demonstrates how you can accomplish that. -``` -Person p = new Person(); -p.setName("Ricky"); -Message message = MessageBuilder.withPayload(p).setHeader("someHeader", "foo").build(); - -Message result = rsocketRequesterBuilder.tcp("localhost", port) - .route("pojoMessageToPojo") - .data(message) - .retrieveMono(new ParameterizedTypeReference>() {}) - .block(); -``` -Aside from sending `Message`, note the usage of `ParameterizedTypeReference` to specify that we want not only `Message` in return but also `Message` with specific payload type. - -### Function Composition over RSocket (Distributed Function Composition) - -By now you shoudl be familiar with the standard function composition feature (e.g., `functionA|functionB|functionC`). This feature allows you to compose several co-located functions into one. But what if these functions are not co-located and instead separated by the network? - -With RSocket and our _distributed function composition_ feature you can still do it. So let's look at the example. - -Let's say we have `uppercase` function available to you locally and `reverse` function exposed via separate RSocket and you wan to compose `uppercase` and `reverse` into a single function. Had they been both available locally it would have been as simple as `uppercase|reverse`, but given that `reverse` function is not locally available we need a way to specify that in our composition instruction. For that we're using _redirect_ operator. So it woudl look like this `uppercase>localhost:2222`, where `localhost:2222` is the host/port combination where `reverse` function is hosted. -What's interesting is that remote function can in itself be a result of function composition (local or remote), so effectively you are composing `uppercase` with whatever function definition (which could be composition) that is running on `localhost:2222`. - -The complete example is available in [this test case](https://github.com/spring-cloud/spring-cloud-function/blob/0e3a27a392f5c69727d909db26c2ba6aa0344cfd/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationTests.java#L371). -And as you can see it is a bit more complex to showcase this feature. In this test we are composing `reverse` function with `uppercase|concat` function running remotely and then with `wrap` function running locally as if `reverse|uppercase|concat|wrap`. -So you can see `--spring.cloud.function.definition=reverse>localhost:" + portA + "|wrap"` where `localhost:" + portA` points to another application context instance with `--spring.cloud.function.definition=uppercase|concat`. The result of the `reverse` function are sent to `uppercase|concat` function via RSocket and the result of that are fed into `wrap` function. - -### Samples - -You can also look at one of the [RSocket samples](https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-cloudevent-rsocket) that is also introduces you to Cloud Events diff --git a/spring-cloud-function-rsocket/pom.xml b/spring-cloud-function-rsocket/pom.xml deleted file mode 100644 index 9b238e2b7..000000000 --- a/spring-cloud-function-rsocket/pom.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-rsocket - jar - Spring Cloud Function RSocket Support - Spring Cloud Function RSocket Support - - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - - - 0.3.0 - - - - - org.springframework.boot - spring-boot-starter-rsocket - - - io.rsocket - rsocket-core - - - io.rsocket - rsocket-transport-netty - - - org.springframework.cloud - spring-cloud-function-context - - - io.rsocket.broker - rsocket-broker-client-spring - ${rsocket-broker.version} - true - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.boot - spring-boot-starter-test - test - - - io.projectreactor - reactor-test - test - - - io.rsocket.broker - rsocket-broker-spring - ${rsocket-broker.version} - test - - - - diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/FunctionRSocketMessageHandler.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/FunctionRSocketMessageHandler.java deleted file mode 100644 index a61e006f0..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/FunctionRSocketMessageHandler.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -import io.rsocket.frame.FrameType; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.MessageRoutingCallback; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.core.MethodParameter; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.core.ReactiveAdapterRegistry; -import org.springframework.core.ResolvableType; -import org.springframework.core.codec.ByteArrayDecoder; -import org.springframework.core.codec.Decoder; -import org.springframework.core.codec.Encoder; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.MessagingException; -import org.springframework.messaging.handler.CompositeMessageCondition; -import org.springframework.messaging.handler.DestinationPatternsMessageCondition; -import org.springframework.messaging.handler.MessageCondition; -import org.springframework.messaging.handler.invocation.reactive.HandlerMethodArgumentResolver; -import org.springframework.messaging.handler.invocation.reactive.HandlerMethodReturnValueHandler; -import org.springframework.messaging.handler.invocation.reactive.SyncHandlerMethodArgumentResolver; -import org.springframework.messaging.rsocket.DefaultMetadataExtractor; -import org.springframework.messaging.rsocket.MetadataExtractor; -import org.springframework.messaging.rsocket.annotation.support.RSocketFrameTypeMessageCondition; -import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler; -import org.springframework.messaging.rsocket.annotation.support.RSocketPayloadReturnValueHandler; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.CollectionUtils; -import org.springframework.util.MimeTypeUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.RouteMatcher; -import org.springframework.util.RouteMatcher.Route; -import org.springframework.util.StringUtils; -import org.springframework.web.util.pattern.PathPatternRouteMatcher; - -/** - * An {@link RSocketMessageHandler} extension for Spring Cloud Function specifics. - * - * @author Artem Bilan - * @author Oleg Zhurakousky - * - * @since 3.1 - */ -class FunctionRSocketMessageHandler extends RSocketMessageHandler { - - public static final String RECONCILED_LOOKUP_DESTINATION_HEADER = "reconciledLookupDestination"; - - private final FunctionCatalog functionCatalog; - - private final FunctionProperties functionProperties; - - private final Field headersField; - - private final JsonMapper jsonMapper; - - private static final Method FUNCTION_APPLY_METHOD = - ReflectionUtils.findMethod(Function.class, "apply", (Class[]) null); - - private static final RSocketFrameTypeMessageCondition REQUEST_CONDITION = - new RSocketFrameTypeMessageCondition( - FrameType.REQUEST_FNF, - FrameType.REQUEST_RESPONSE, - FrameType.REQUEST_STREAM, - FrameType.REQUEST_CHANNEL); - - FunctionRSocketMessageHandler(FunctionCatalog functionCatalog, FunctionProperties functionProperties, JsonMapper jsonMapper) { - setHandlerPredicate((clazz) -> false); - this.functionCatalog = functionCatalog; - this.functionProperties = functionProperties; - this.headersField = ReflectionUtils.findField(MessageHeaders.class, "headers"); - this.headersField.setAccessible(true); - this.jsonMapper = jsonMapper; - } - - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void afterPropertiesSet() { - List encoders = this.getEncoders(); - encoders.set(0, new MessageAwareJsonEncoder(this.jsonMapper)); - super.afterPropertiesSet(); - } - - @SuppressWarnings("unchecked") - @Override - public MetadataExtractor getMetadataExtractor() { - return new HeadersAwareMetadataExtractor((List>) this.getDecoders()); - } - - /** - * Will check if there is a function handler registered for destination before proceeding. - * This typically happens when user avoids using 'spring.cloud.function.definition' property. - */ - @Override - public Mono handleMessage(Message message) throws MessagingException { - if (!FrameType.SETUP.equals(message.getHeaders().get("rsocketFrameType"))) { - String destination = this.discoverAndInjectDestinationHeader(message); - - Set mappings = this.getDestinationLookup().keySet(); - if (!mappings.contains(destination)) { - FunctionInvocationWrapper function = FunctionRSocketUtils - .registerFunctionForDestination(destination, this.functionCatalog, this.getApplicationContext()); - this.registerFunctionHandler(new RSocketListenerFunction(function), destination); - } - } - - return super.handleMessage(message); - } - - @Override - protected RouteMatcher.Route getDestination(Message message) { - RouteMatcher.Route reconsiledDestination = (RouteMatcher.Route) message.getHeaders().get(RECONCILED_LOOKUP_DESTINATION_HEADER); - return reconsiledDestination == null ? super.getDestination(message) : reconsiledDestination; - } - - @Override - protected CompositeMessageCondition getMatchingMapping(CompositeMessageCondition mapping, Message message) { - List> result = new ArrayList<>(mapping.getMessageConditions().size()); - for (MessageCondition condition : mapping.getMessageConditions()) { - MessageCondition matchingCondition = condition instanceof DestinationPatternsMessageCondition - ? condition - : (MessageCondition) condition.getMatchingCondition(message); - if (matchingCondition == null) { - return null; - } - result.add(matchingCondition); - } - return new CompositeMessageCondition(result.toArray(new MessageCondition[0])); - } - - void registerFunctionHandler(Function function, String route) { - CompositeMessageCondition condition = - new CompositeMessageCondition(REQUEST_CONDITION, - new DestinationPatternsMessageCondition(new String[]{ route }, - obtainRouteMatcher())); - registerHandlerMethod(function, FUNCTION_APPLY_METHOD, condition); - } - - @Override - protected List initArgumentResolvers() { - List resolvers = super.initArgumentResolvers(); - return Collections.singletonList(new MessageHandlerMethodArgumentResolver(this.jsonMapper, resolvers)); - } - - @SuppressWarnings("unchecked") - @Override - protected List initReturnValueHandlers() { - return Collections.singletonList(new FunctionRSocketPayloadReturnValueHandler((List>) getEncoders(), - getReactiveAdapterRegistry())); - } - - private String discoverAndInjectDestinationHeader(Message message) { - - String destination; - if (!CollectionUtils.isEmpty(this.getApplicationContext().getBeansOfType(MessageRoutingCallback.class))) { - destination = RoutingFunction.FUNCTION_NAME; - } - else if (StringUtils.hasText(this.functionProperties.getRoutingExpression())) { - destination = RoutingFunction.FUNCTION_NAME; - this.updateMessageHeaders(message, destination); - } - else { - Route route = (Route) message.getHeaders().get(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER); - destination = route.value(); - if (!StringUtils.hasText(destination)) { - destination = this.functionProperties.getDefinition(); - this.updateMessageHeaders(message, destination); - } - } - - if (!StringUtils.hasText(destination) && logger.isDebugEnabled()) { - logger.debug("Failed to discover function definition. Neither " - + "`spring.cloud.function.definition`, nor `.route()`, nor " - + "`spring.cloud.function.routing-expression` were provided. Will use empty string " - + "for lookup, which will work only if there is one function in Function Catalog"); - } - return destination; - } - - @SuppressWarnings("unchecked") - private void updateMessageHeaders(Message message, String destination) { - Map headersMap = (Map) ReflectionUtils - .getField(this.headersField, message.getHeaders()); - PathPatternRouteMatcher matcher = new PathPatternRouteMatcher(); - headersMap.put(RECONCILED_LOOKUP_DESTINATION_HEADER, matcher.parseRoute(destination)); - } - - protected static final class MessageHandlerMethodArgumentResolver implements SyncHandlerMethodArgumentResolver { - - private final Decoder decoder; - - private final JsonMapper jsonMapper; - - private final List resolvers; - - MessageHandlerMethodArgumentResolver(JsonMapper jsonMapper, List resolvers) { - this.decoder = new ByteArrayDecoder(); - this.jsonMapper = jsonMapper; - this.resolvers = resolvers; - } - - @Override - public boolean supportsParameter(MethodParameter parameter) { - return true; - } - - @SuppressWarnings("unchecked") - @Override - public Object resolveArgumentValue(MethodParameter parameter, - Message message) { - Object payload = message.getPayload(); - Flux data = payload instanceof DataBuffer - ? Flux.just((DataBuffer) payload) - : Flux.from((Publisher) payload); - - if (message.getHeaders().containsKey(MessageHeaders.CONTENT_TYPE) - && MimeTypeUtils.APPLICATION_JSON_VALUE.equals(message.getHeaders().get(MessageHeaders.CONTENT_TYPE).toString())) { - Flux argument = data.map(buffer -> { - byte[] bytePayload = this.decoder.decode(buffer, ResolvableType.forType(byte[].class), null, null); - if (JsonMapper.isJsonString(bytePayload)) { -// // could be array, map or string - Object structure = this.jsonMapper.fromJson(bytePayload, Object.class); - if (structure instanceof Map && ((Map) structure).containsKey(FunctionRSocketUtils.PAYLOAD)) { - return MessageBuilder.withPayload(((Map) structure).remove(FunctionRSocketUtils.PAYLOAD)) - .copyHeaders((Map) ((Map) structure).get(FunctionRSocketUtils.HEADERS)) - .build(); - } - } - return MessageBuilder.withPayload(bytePayload).copyHeadersIfAbsent(message.getHeaders()).build(); - }); - return MessageBuilder.createMessage(argument, message.getHeaders()); - } - else { // delegate to the existing argument resolvers - for (HandlerMethodArgumentResolver handlerMethodArgumentResolver : this.resolvers) { - if (handlerMethodArgumentResolver.supportsParameter(parameter)) { - Publisher arg = handlerMethodArgumentResolver.resolveArgument(parameter, message); - return MessageBuilder.withPayload(arg).copyHeadersIfAbsent(message.getHeaders()).build(); - } - } - return message; - } - } - } - - protected static final class FunctionRSocketPayloadReturnValueHandler extends RSocketPayloadReturnValueHandler { - - public FunctionRSocketPayloadReturnValueHandler(List> encoders, ReactiveAdapterRegistry registry) { - super(encoders, registry); - } - - @Override - public Mono handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, - Message message) { - - if (returnValue instanceof Publisher && !message.getHeaders().containsKey(RESPONSE_HEADER)) { - return Mono.from((Publisher) returnValue).then(); - } - return super.handleReturnValue(returnValue, returnType, message); - } - } - - /** - * This metadata extractor will ensure that any JSON data passed - * via metadata will be copied into Message headers. - */ - private static class HeadersAwareMetadataExtractor extends DefaultMetadataExtractor { - HeadersAwareMetadataExtractor(List> decoders) { - super(decoders); - super.metadataToExtract(MimeTypeUtils.APPLICATION_JSON, - new ParameterizedTypeReference>() { - }, (jsonMap, outputMap) -> outputMap.putAll(jsonMap) - ); - } - } - -} diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/FunctionRSocketUtils.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/FunctionRSocketUtils.java deleted file mode 100644 index a46e482f4..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/FunctionRSocketUtils.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.lang.reflect.Type; -import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Pattern; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.context.ApplicationContext; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.rsocket.RSocketRequester; -import org.springframework.messaging.rsocket.RSocketRequester.Builder; -import org.springframework.util.Assert; -import org.springframework.util.MimeTypeUtils; -import org.springframework.util.StringUtils; - -/** - * - * @author Oleg Zhurakousky - * - * @since 3.1 - * - */ -final class FunctionRSocketUtils { - - private static final Log LOGGER = LogFactory.getLog(FunctionRSocketUtils.class); - - public static String PAYLOAD = "payload"; - - public static String HEADERS = "headers"; - - - private static final Pattern WS_URI_PATTERN = Pattern.compile("^(https?|wss?)://.+"); - - private FunctionRSocketUtils() { - - } - - static FunctionInvocationWrapper registerFunctionForDestination(String functionDefinition, FunctionCatalog functionCatalog, - ApplicationContext applicationContext) { - - registerRSocketForwardingFunctionIfNecessary(functionDefinition, functionCatalog, applicationContext); - FunctionProperties functionProperties = applicationContext.getBean(FunctionProperties.class); - String acceptContentType = functionProperties.getExpectedContentType(); - if (!StringUtils.hasText(acceptContentType)) { - FunctionInvocationWrapper function = functionCatalog.lookup(functionDefinition); - Type outputType = function.getOutputType(); - acceptContentType = (outputType instanceof Class && String.class.isAssignableFrom((Class) outputType)) - ? MimeTypeUtils.TEXT_PLAIN_VALUE : MimeTypeUtils.APPLICATION_JSON_VALUE; - } - - FunctionInvocationWrapper function = functionCatalog.lookup(functionDefinition, acceptContentType); - function.setSkipOutputConversion(true); - return function; - } - - static void registerRSocketForwardingFunctionIfNecessary(String definition, FunctionCatalog functionCatalog, - ApplicationContext applicationContext) { - String[] names = StringUtils.delimitedListToStringArray(definition.replaceAll(",", "|").trim(), "|"); - for (String name : names) { - - if (functionCatalog.lookup(name) == null) { // this means RSocket - String[] functionToRSocketDefinition = StringUtils.delimitedListToStringArray(name, ">"); - if (functionToRSocketDefinition.length == 1) { - return; - } - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Registering RSocket forwarder for '" + name + "' function."); - } - - Assert.isTrue(functionToRSocketDefinition.length == 2, "Must only contain one output redirect. Was '" + name + "'."); - FunctionInvocationWrapper function = functionCatalog.lookup(functionToRSocketDefinition[0], MimeTypeUtils.APPLICATION_JSON_VALUE); - - String[] hostPort = StringUtils.delimitedListToStringArray(functionToRSocketDefinition[1], ":"); - - String forwardingUrl = functionToRSocketDefinition[1]; - Builder rsocketRequesterBuilder = applicationContext.getBean(Builder.class); - - RSocketRequester rsocketRequester = (WS_URI_PATTERN.matcher(forwardingUrl).matches()) - ? rsocketRequesterBuilder.websocket(URI.create(forwardingUrl)) - : rsocketRequesterBuilder.tcp(hostPort[0], Integer.parseInt(hostPort[1])); - - RSocketForwardingFunction rsocketFunction = - new RSocketForwardingFunction(function, rsocketRequester, null); - FunctionRegistration functionRegistration = - new FunctionRegistration<>(rsocketFunction, name); - functionRegistration.type( - FunctionTypeUtils.discoverFunctionTypeFromClass(RSocketForwardingFunction.class)); - ((FunctionRegistry) functionCatalog).register(functionRegistration); - } - } - } - - static Map sanitizeMessageToMap(Message message) { - Map messageMap = new HashMap<>(); - messageMap.put(PAYLOAD, message.getPayload()); - Map headers = new HashMap<>(); - for (String key : message.getHeaders().keySet()) { - if (key.equals("lookupDestination") || - key.equals("reconciledLookupDestination") || - key.equals(MessageHeaders.CONTENT_TYPE)) { - headers.put(key, message.getHeaders().get(key).toString()); - } - else if (!key.equals("rsocketRequester")) { - headers.put(key, message.getHeaders().get(key)); - } - } - messageMap.put(HEADERS, headers); - return messageMap; - } -} diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/MessageAwareJsonDecoder.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/MessageAwareJsonDecoder.java deleted file mode 100644 index 1398d3958..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/MessageAwareJsonDecoder.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; - -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.core.ResolvableType; -import org.springframework.core.codec.AbstractDecoder; -import org.springframework.core.codec.DecodingException; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.lang.Nullable; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeType; -import org.springframework.util.MimeTypeUtils; - -/** - * - * @author Oleg Zhurakousky - * @since 3.1 - * - */ -class MessageAwareJsonDecoder extends AbstractDecoder { - - private final JsonMapper jsonMapper; - - MessageAwareJsonDecoder(JsonMapper jsonMapper) { - super(MimeTypeUtils.APPLICATION_JSON); - this.jsonMapper = jsonMapper; - } - - @Override - public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) { - return mimeType != null && mimeType.isCompatibleWith(MimeTypeUtils.APPLICATION_JSON); - } - - @SuppressWarnings("unchecked") - @Override - public Object decode(DataBuffer dataBuffer, ResolvableType targetType, - @Nullable MimeType mimeType, @Nullable Map hints) - throws DecodingException { - - ResolvableType type = ResolvableType.forClassWithGenerics(Map.class, String.class, - Object.class); - Map messageMap = (Map) doDecode(dataBuffer, type, - mimeType, hints); - if (messageMap.containsKey(FunctionRSocketUtils.PAYLOAD)) { - Type requestedType = FunctionTypeUtils.getGenericType(targetType.getType()); - Object payload; - if (String.class.isAssignableFrom(FunctionTypeUtils.getRawType(targetType.getType()))) { - Object rawPayload = messageMap.get(FunctionRSocketUtils.PAYLOAD); - if (rawPayload instanceof byte[]) { - payload = new String((byte[]) rawPayload, StandardCharsets.UTF_8); - } - else { - payload = rawPayload; - } - } - else if (byte[].class.isAssignableFrom(FunctionTypeUtils.getRawType(targetType.getType()))) { - Object rawPayload = messageMap.get(FunctionRSocketUtils.PAYLOAD); - if (rawPayload instanceof String) { - payload = ((String) rawPayload).getBytes(StandardCharsets.UTF_8); - } - else { - payload = rawPayload; - } - } - else { - payload = this.jsonMapper.fromJson(messageMap.get(FunctionRSocketUtils.PAYLOAD), requestedType); - } -// if (String.class.isAssignableFrom(FunctionTypeUtils.getRawType(targetType.getType())) -// || byte[].class.isAssignableFrom(FunctionTypeUtils.getRawType(targetType.getType()))) { -// Object rawPayload = messageMap.get(FunctionRSocketUtils.PAYLOAD); -// if (rawPayload instanceof byte[]) { -// payload = new String((byte[]) rawPayload, StandardCharsets.UTF_8); -// } -// else { -// payload = rawPayload; -// } -// } -// else { -// payload = this.jsonMapper.fromJson(messageMap.get(FunctionRSocketUtils.PAYLOAD), requestedType); -// } - - if (FunctionTypeUtils.isMessage(targetType.getType())) { - return MessageBuilder.withPayload(payload).copyHeaders( - (Map) messageMap.get(FunctionRSocketUtils.HEADERS)) - .build(); - } - else { - return payload; - } - } - else { - return messageMap; - } - } - - private Object doDecode(DataBuffer dataBuffer, ResolvableType targetType, - @Nullable MimeType mimeType, @Nullable Map hints) - throws DecodingException { - - try { - byte[] data = toByteArray(dataBuffer.asInputStream()); - if (JsonMapper.isJsonStringRepresentsMap(data)) { - return this.jsonMapper.fromJson(data, targetType.getType()); - } - else { - Map messageMap = new HashMap<>(); - messageMap.put(FunctionRSocketUtils.PAYLOAD, data); - return messageMap; - } - } - catch (IOException ex) { - throw new IllegalStateException(ex); - } - finally { - DataBufferUtils.release(dataBuffer); - } - } - - private byte[] toByteArray(final InputStream input) throws IOException { - try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { - copyLarge(input, output, new byte[2048]); - return output.toByteArray(); - } - } - - private long copyLarge(final InputStream input, final OutputStream output, - final byte[] buffer) throws IOException { - long count = 0; - int n; - while (-1 != (n = input.read(buffer))) { - output.write(buffer, 0, n); - count += n; - } - return count; - } - - @Override - public Flux decode(Publisher inputStream, - ResolvableType elementType, MimeType mimeType, Map hints) { - return Flux.from(inputStream).map(buffer -> decode(buffer, elementType, mimeType, hints)); - } -} diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/MessageAwareJsonEncoder.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/MessageAwareJsonEncoder.java deleted file mode 100644 index 02aa41045..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/MessageAwareJsonEncoder.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; - -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.core.ResolvableType; -import org.springframework.core.codec.AbstractEncoder; -import org.springframework.core.codec.ByteArrayEncoder; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferFactory; -import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.util.MimeType; -import org.springframework.util.MimeTypeUtils; - - - -/** - * @author Oleg Zhurakousky - * @since 3.1 - * - */ -/* - * We basically don't need it, but having it allows us not to depend on spring-web - */ -class MessageAwareJsonEncoder extends AbstractEncoder { - - private final JsonMapper mapper; - - private final boolean isClient; - - private final ByteArrayEncoder byteArrayEncoder; - - MessageAwareJsonEncoder(JsonMapper mapper) { - this(mapper, false); - } - - MessageAwareJsonEncoder(JsonMapper mapper, boolean isClient) { - super(MimeTypeUtils.APPLICATION_JSON); - this.mapper = mapper; - this.isClient = isClient; - this.byteArrayEncoder = new ByteArrayEncoder(); - } - - @Override - public boolean canEncode(ResolvableType elementType, MimeType mimeType) { - boolean canEncode = mimeType != null && mimeType.isCompatibleWith(MimeTypeUtils.APPLICATION_JSON); - if (canEncode && this.isClient) { - canEncode = (FunctionTypeUtils.isMessage(elementType.getType()) - || Map.class.isAssignableFrom(FunctionTypeUtils.getRawType(elementType.getType()))); - } - return canEncode; - } - - - @Override - public List getEncodableMimeTypes() { - return Collections.singletonList(MimeTypeUtils.APPLICATION_JSON); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public DataBuffer encodeValue(Object value, DataBufferFactory bufferFactory, - ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map hints) { - - if (value instanceof Message) { - Object payload = ((Message) value).getPayload(); - value = FunctionRSocketUtils.sanitizeMessageToMap((Message) value); - if (payload instanceof byte[]) { - payload = new String((byte[]) payload, StandardCharsets.UTF_8); // safe for cases when we have JSON - ((Map) value).put(FunctionRSocketUtils.PAYLOAD, payload); - } - } - else if (!(value instanceof Map)) { - if (JsonMapper.isJsonString(value)) { - value = this.mapper.fromJson(value, valueType.getType()); - } - value = Collections.singletonMap(FunctionRSocketUtils.PAYLOAD, value); - } - byte[] data = this.mapper.toJson(value); - return this.byteArrayEncoder.encodeValue(data, bufferFactory, valueType, mimeType, hints); - } - - @Override - public Flux encode(Publisher inputStream, - DataBufferFactory bufferFactory, ResolvableType elementType, - MimeType mimeType, Map hints) { - return Flux.from(inputStream).map(value -> - encodeValue(value, bufferFactory, elementType, mimeType, hints)); - } -} diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketAutoConfiguration.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketAutoConfiguration.java deleted file mode 100644 index e7cc2b28a..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketAutoConfiguration.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.rsocket.RSocketMessageHandlerCustomizer; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.messaging.rsocket.RSocketStrategies; - -/** - * Main configuration class for components required to support RSocket integration with - * spring-cloud-function. - * - * @author Oleg Zhurakousky - * @author Artem Bilan - * - * @since 3.1 - */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(FunctionProperties.class) -@ConditionalOnProperty(name = FunctionProperties.PREFIX + ".rsocket.enabled", matchIfMissing = true) -class RSocketAutoConfiguration { - - @Bean - @ConditionalOnMissingBean - @Primary - FunctionRSocketMessageHandler functionRSocketMessageHandler(RSocketStrategies rSocketStrategies, - ObjectProvider customizers, FunctionCatalog functionCatalog, - FunctionProperties functionProperties, JsonMapper jsonMapper) { - - FunctionRSocketMessageHandler rsocketMessageHandler = new FunctionRSocketMessageHandler(functionCatalog, functionProperties, jsonMapper); - rsocketMessageHandler.setRSocketStrategies(rSocketStrategies); - customizers.orderedStream().forEach((customizer) -> customizer.customize(rsocketMessageHandler)); - return rsocketMessageHandler; - } -} diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketCustomizerConfiguration.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketCustomizerConfiguration.java deleted file mode 100644 index 82628e186..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketCustomizerConfiguration.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.rsocket.messaging.RSocketStrategiesCustomizer; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.rsocket.RSocketStrategies.Builder; - -/** - * @author Oleg Zhurakousky - * - * @since 3.2 - */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties({ FunctionProperties.class, RSocketFunctionProperties.class }) -public class RSocketCustomizerConfiguration { - - @Bean - RSocketStrategiesCustomizer rSocketStrategiesCustomizer(JsonMapper jsonMapper) { - return new RSocketStrategiesCustomizer() { - @Override - public void customize(Builder strategies) { - strategies - .encoders(encoders -> { - encoders.add(0, new MessageAwareJsonEncoder(jsonMapper, true)); - }) - .decoders(decoders -> { - decoders.add(0, new MessageAwareJsonDecoder(jsonMapper)); - }); - } - }; - } -} diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketForwardingFunction.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketForwardingFunction.java deleted file mode 100644 index 8da7e76e4..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketForwardingFunction.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.util.function.Function; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Mono; - -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.messaging.Message; -import org.springframework.messaging.rsocket.RSocketRequester; -import org.springframework.messaging.support.GenericMessage; - - -/** - * Wrapper over an instance of target Function (represented by {@link FunctionInvocationWrapper}) - * which will use the result of the invocation of such function as an input to another RSocket - * effectively composing two functions over RSocket. - *

- * Note: the remote RSocket route is not required to represent Spring Cloud Function binding. - * - * @author Oleg Zhurakousky - * @author Artem Bilan - * - * @since 3.1 - * - */ -class RSocketForwardingFunction implements Function, Publisher>> { - - private static final Log LOGGER = LogFactory.getLog(RSocketForwardingFunction.class); - - private final FunctionInvocationWrapper targetFunction; - - private final RSocketRequester rSocketRequester; - - RSocketForwardingFunction(FunctionInvocationWrapper targetFunction, RSocketRequester rsocketRequester, - String remoteFunctionName) { - - this.targetFunction = targetFunction; - this.rSocketRequester = rsocketRequester; - } - - @Override - public Publisher> apply(Message input) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Executing: " + this.targetFunction); - } - - Mono targetFunctionCall = Mono.just(input) - .map(this.targetFunction) - .cast(Message.class) - .map(Message::getPayload); - - return this.rSocketRequester - .route("") - .data(targetFunctionCall, byte[].class) - .retrieveFlux(byte[].class) - .map(GenericMessage::new); - } -} diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketFunctionProperties.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketFunctionProperties.java deleted file mode 100644 index f6c137ccc..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketFunctionProperties.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.function.context.FunctionProperties; - -/** - * Main configuration properties for RSocket integration with spring-cloud-function. - * The prefix for these properties is `spring.cloud.function.rscocket`. - * - * @author Oleg Zhurakousky - * @author Spencer Gibb - * @since 3.1 - */ -@ConfigurationProperties(prefix = FunctionProperties.PREFIX + ".rsocket") -public class RSocketFunctionProperties { - - private boolean enabled; - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } -} diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketListenerFunction.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketListenerFunction.java deleted file mode 100644 index 53f81e688..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketListenerFunction.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.util.Map; -import java.util.function.Function; - -import io.rsocket.frame.FrameType; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.rsocket.annotation.support.RSocketFrameTypeMessageCondition; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.Assert; -import org.springframework.util.MimeTypeUtils; - -/** - * A function wrapper which is bound onto an RSocket route. - * - * @author Oleg Zhurakousky - * @author Artem Bilan - * - * @since 3.1 - */ -class RSocketListenerFunction implements Function> { - - private final FunctionInvocationWrapper targetFunction; - - RSocketListenerFunction(FunctionInvocationWrapper targetFunction) { - Assert.isTrue(targetFunction != null, "Failed to discover target function. \n" - + "To fix it you should either provide 'spring.cloud.function.definition' property " - + "or if you are using RSocketRequester provide valid function definition via 'route' " - + "operator (e.g., requester.route(\"echo\"))"); - this.targetFunction = targetFunction; - } - - - @SuppressWarnings("unchecked") - @Override - public Publisher apply(Object input) { - /* - * We need to maintain the input typeless to ensure that no encoder/decoders will attempt any conversion. - * That said it will always be Message> - */ - Message> inputMessage = (Message>) input; - - FrameType frameType = RSocketFrameTypeMessageCondition.getFrameType(inputMessage); - switch (frameType) { - case REQUEST_FNF: - return handle(inputMessage); - case REQUEST_RESPONSE: - case REQUEST_STREAM: - case REQUEST_CHANNEL: - return handleAndReply(inputMessage); - default: - throw new UnsupportedOperationException(); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private Mono handle(Message> messageToProcess) { - if (this.targetFunction.isRoutingFunction()) { - Flux dataFlux = Flux.from(messageToProcess.getPayload()) - .map(payload -> MessageBuilder.createMessage(payload, messageToProcess.getHeaders())); - return dataFlux.doOnNext(this.targetFunction).then(); - } - else if (this.targetFunction.isConsumer()) { - Flux dataFlux = Flux.from(messageToProcess.getPayload()) - .map(payload -> this.buildReceivedMessage(payload, messageToProcess.getHeaders())); - - dataFlux = FunctionTypeUtils.isPublisher(this.targetFunction.getInputType()) - ? dataFlux.transform((Function) this.targetFunction) - : dataFlux.doOnNext(this.targetFunction); - - return dataFlux.then(); - } - else { - return Mono.error(new IllegalStateException("Only 'Consumer' can handle 'fire-and-forget' RSocket frame.")); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private Flux handleAndReply(Message> messageToProcess) { - Flux dataFlux = Flux.from(messageToProcess.getPayload()) - .map(payload -> this.buildReceivedMessage(payload, messageToProcess.getHeaders())); - - if (this.targetFunction.getInputType() != null && FunctionTypeUtils.isPublisher(this.targetFunction.getInputType())) { - dataFlux = dataFlux.transform((Function) this.targetFunction); - } - else { - dataFlux = dataFlux.flatMap((data) -> { - Map messageMap = FunctionRSocketUtils.sanitizeMessageToMap((Message) data); - Message sanitizedMessage = MessageBuilder.withPayload(messageMap.remove(FunctionRSocketUtils.PAYLOAD)) - .copyHeaders((Map) messageMap.get(FunctionRSocketUtils.HEADERS)) - .build(); - Object result = this.targetFunction.isSupplier() ? this.targetFunction.apply(null) : this.targetFunction.apply(sanitizedMessage); - - Publisher resultPublisher = result instanceof Publisher - ? (Publisher) result - : Mono.just(result); - return Flux.from(resultPublisher).map(v -> extractPayloadIfNecessary(v)); - }); - } - return dataFlux; - } - - private Message buildReceivedMessage(Object mayBeMessage, MessageHeaders messageHeaders) { - return mayBeMessage instanceof Message - ? MessageBuilder.fromMessage((Message) mayBeMessage).copyHeadersIfAbsent(messageHeaders).build() - : MessageBuilder.withPayload(mayBeMessage).copyHeadersIfAbsent(messageHeaders).build(); - } - - /* - * This will ensure that unless CT is application/json for which we provide Message aware encoder/decoder - * the payload is extracted since no other available encoders/decoders understand Message. - */ - private Object extractPayloadIfNecessary(Object output) { - if (output instanceof Message) { - Message resultMessage = (Message) output; - Object contentType = resultMessage.getHeaders().get(MessageHeaders.CONTENT_TYPE); - if (contentType != null && contentType.toString().equals(MimeTypeUtils.APPLICATION_JSON_VALUE)) { - return output; - } - else { - return resultMessage.getPayload(); - } - } - return output; - } -} diff --git a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketRoutingAutoConfiguration.java b/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketRoutingAutoConfiguration.java deleted file mode 100644 index abf37dbd2..000000000 --- a/spring-cloud-function-rsocket/src/main/java/org/springframework/cloud/function/rsocket/RSocketRoutingAutoConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import io.rsocket.broker.client.spring.BrokerClientAutoConfiguration; - -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.rsocket.RSocketConnectorConfigurer; - -/** - * Configuration for components required to support RSocket Routing Client - * integration with spring-cloud-function. - * - * @author Spencer Gibb - * @since 3.1 - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass(BrokerClientAutoConfiguration.class) -@ConditionalOnProperty(name = FunctionProperties.PREFIX + ".rsocket.enabled", matchIfMissing = true) -@AutoConfigureBefore(BrokerClientAutoConfiguration.class) -@AutoConfigureAfter(RSocketAutoConfiguration.class) -class RSocketRoutingAutoConfiguration { - - @Bean - RSocketConnectorConfigurer functionRSocketConnectorConfigurer( - FunctionRSocketMessageHandler handler) { - return connector -> connector.acceptor(handler.responder()); - } - -} diff --git a/spring-cloud-function-rsocket/src/main/resources/META-INF/spring.factories b/spring-cloud-function-rsocket/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 8b1378917..000000000 --- a/spring-cloud-function-rsocket/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring-cloud-function-rsocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-function-rsocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index fdb2838db..000000000 --- a/spring-cloud-function-rsocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.cloud.function.rsocket.RSocketAutoConfiguration -org.springframework.cloud.function.rsocket.RSocketCustomizerConfiguration diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessageRoutingCallbackRSocketTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessageRoutingCallbackRSocketTests.java deleted file mode 100644 index 349566c1e..000000000 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessageRoutingCallbackRSocketTests.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.MessageRoutingCallback; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.messaging.rsocket.RSocketRequester; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeTypeUtils; - -/** - * - * @author Oleg Zhurakousky - * @author Chris Bono - */ -public class MessageRoutingCallbackRSocketTests { - - @Test - public void testRoutingWithRoutingCallback() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(RoutingCallbackFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.expected-content-type=text/plain", - "--spring.rsocket.server.port=0"); - ) { - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - int port = applicationContext.getEnvironment().getProperty("local.rsocket.server.port", Integer.class); - - // imperative - rsocketRequesterBuilder.tcp("localhost", port) - .route("foo") - .metadata("{\"func_name\":\"uppercase\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - - // imperative Message - rsocketRequesterBuilder.tcp("localhost", port) - .route("foo") - .metadata("{\"func_name\":\"uppercaseMessage\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - - // reactive - rsocketRequesterBuilder.tcp("localhost", port) - .route("foo") - .metadata("{\"func_name\":\"uppercaseReactive\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - - // reactive - rsocketRequesterBuilder.tcp("localhost", port) - .route("foo") - .metadata("{\"func_name\":\"uppercaseReactiveMessage\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - } - } - - @EnableAutoConfiguration - @Configuration - public static class RoutingCallbackFunctionConfiguration { - @Bean - public MessageRoutingCallback customRouter() { - return new MessageRoutingCallback() { - @Override - public String routingResult(Message message) { - return (String) message.getHeaders().get("func_name"); - } - }; - } - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - @Bean - public Function, Message> uppercaseMessage() { - return m -> MessageBuilder.withPayload(m.getPayload().toUpperCase()).copyHeaders(m.getHeaders()).build(); - } - - @Bean - public Function, Flux> uppercaseReactive() { - return flux -> flux.map(v -> v.toUpperCase()); - } - - @Bean - public Function>, Flux>> uppercaseReactiveMessage() { - return flux -> flux.map(m -> MessageBuilder.withPayload(m.getPayload().toUpperCase()).copyHeaders(m.getHeaders()).build()); - } - - @Bean - public Function concat() { - return v -> v + v; - } - } -} diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessagingTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessagingTests.java deleted file mode 100644 index 5238a87ba..000000000 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessagingTests.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.util.Map; -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.messaging.Message; -import org.springframework.messaging.rsocket.RSocketRequester; -import org.springframework.messaging.support.MessageBuilder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * @author Chris Bono - */ -public class MessagingTests { - - @Test - public void testPojoToStringViaMessage() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(MessagingConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - Person p = new Person(); - p.setName("Ricky"); - Message message = MessageBuilder.withPayload(p).setHeader("someHeader", "foo").build(); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("pojoToString") - .data(message) - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("RICKY") - .expectComplete() - .verify(); - } - } - - @SuppressWarnings("rawtypes") - @Test - public void testPojoToStringViaMessageMap() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(MessagingConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - Person p = new Person(); - p.setName("Ricky"); - Message message = MessageBuilder.withPayload(p).setHeader("someHeader", "foo").build(); - - JsonMapper jsonMapper = applicationContext.getBean(JsonMapper.class); - Map map = jsonMapper.fromJson(message, Map.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("pojoToString") - .data(map) - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("RICKY") - .expectComplete() - .verify(); - } - } - - @Test - public void testPojoToStringViaMessageExpectMessage() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(MessagingConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - Person p = new Person(); - p.setName("Ricky"); - Message message = MessageBuilder.withPayload(p).setHeader("someHeader", "foo").build(); - - Message result = rsocketRequesterBuilder.tcp("localhost", port) - .route("pojoToString") - .data(message) - .retrieveMono(new ParameterizedTypeReference>() { - }) - .block(); - - assertThat(result.getPayload()).isEqualTo("RICKY"); - assertThat(result.getHeaders().get("someHeader")).isEqualTo("foo"); - } - } - - @Test - public void testPojoMessageToPojoViaMessage() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(MessagingConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - Person p = new Person(); - p.setName("Ricky"); - Message message = MessageBuilder.withPayload(p).setHeader("someHeader", "foo").build(); - - Person result = new Person(); - result.setName(p.getName().toUpperCase()); - rsocketRequesterBuilder.tcp("localhost", port) - .route("pojoMessageToPojo") - .data(message) - .retrieveMono(Person.class) - .as(StepVerifier::create) - .expectNext(result) - .expectComplete() - .verify(); - } - } - - @SuppressWarnings("rawtypes") - @Test - public void testPojoMessageToPojoViaMap() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(MessagingConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - Person p = new Person(); - p.setName("Ricky"); - Message message = MessageBuilder.withPayload(p).setHeader("someHeader", "foo").build(); - - JsonMapper jsonMapper = applicationContext.getBean(JsonMapper.class); - Map map = jsonMapper.fromJson(message, Map.class); - - Person result = new Person(); - result.setName(p.getName().toUpperCase()); - rsocketRequesterBuilder.tcp("localhost", port) - .route("pojoMessageToPojo") - .data(map) - .retrieveMono(Person.class) - .as(StepVerifier::create) - .expectNext(result) - .expectComplete() - .verify(); - } - } - - @Test - public void testPojoMessageToPojoViaMessageExpectMessage() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(MessagingConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - Person p = new Person(); - p.setName("Ricky"); - Message message = MessageBuilder.withPayload(p).setHeader("someHeader", "foo").build(); - - Message result = rsocketRequesterBuilder.tcp("localhost", port) - .route("pojoMessageToPojo") - .data(message) - .retrieveMono(new ParameterizedTypeReference>() { - }) - .block(); - - assertThat(result.getPayload().getName()).isEqualTo("RICKY"); - assertThat(result.getHeaders().get("someHeader")).isEqualTo("foo"); - } - } - - @Test - public void testPojoMessageToPojoViaMessageExpectMessageRawPayload() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(MessagingConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - Message message = MessageBuilder.withPayload("{\"name\":\"bob\"}".getBytes()) - .setHeader("someHeader", "foo") - .build(); - - Message result = rsocketRequesterBuilder.tcp("localhost", port) - .route("pojoMessageToPojo") - .data(message) - .retrieveMono(new ParameterizedTypeReference>() { - }) - .block(); - - assertThat(result.getPayload()).isEqualTo("{\"name\":\"BOB\"}".getBytes()); - assertThat(result.getHeaders().get("someHeader")).isEqualTo("foo"); - } - } - - @Test - public void testPojoMessageToPojoViaMessageExpectMessageStringPayload() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(MessagingConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - Message message = MessageBuilder.withPayload("{\"name\":\"bob\"}") - .setHeader("someHeader", "foo") - .build(); - - Message result = rsocketRequesterBuilder.tcp("localhost", port) - .route("pojoMessageToPojo") - .data(message) - .retrieveMono(new ParameterizedTypeReference>() { - }) - .block(); - - assertThat(result.getPayload()).isEqualTo("{\"name\":\"BOB\"}"); - assertThat(result.getHeaders().get("someHeader")).isEqualTo("foo"); - } - } - - @Test - public void testPojoToMessageMap() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(MessagingConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - Person p = new Person(); - p.setName("Ricky"); - - Message> result = rsocketRequesterBuilder.tcp("localhost", port) - .route("echoMessageMap") - .data(p) - .retrieveMono(new ParameterizedTypeReference>>() { - }) - .block(); - - assertThat(((Map) result.getPayload()).get("name")).isEqualTo("Ricky"); - } - } - - private int getLocalRsocketPort(ConfigurableApplicationContext context) { - return context.getEnvironment().getProperty("local.rsocket.server.port", Integer.class); - } - - @EnableAutoConfiguration - @Configuration - public static class MessagingConfiguration { - - @Bean - public Function pojoToString() { - return v -> { - return v.getName().toUpperCase(); - }; - } - - @Bean - public Function>, Message>> echoMessageMap() { - return v -> { - assertThat(v.getHeaders()).containsKey("rsocketFrameType"); - assertThat(v.getHeaders()).containsKey("lookupDestination"); - return v; - }; - } - - @Bean - public Function>>, Flux>>> echoMessageMapReactive() { - return v -> { - return v; - }; - } - - @Bean - public Function, Person> pojoMessageToPojo() { - return p -> { - assertThat(p.getHeaders().get("someHeader").equals("foo")); - Person newPerson = new Person(); - newPerson.setName(p.getPayload().getName().toUpperCase()); - return newPerson; - }; - } - - @Bean - public Function, Message> pojoMessageToPojoMessage() { - return p -> { - assertThat(p.getHeaders().get("someHeader").equals("foo")); - Person newPerson = new Person(); - newPerson.setName(p.getPayload().getName().toUpperCase()); - return MessageBuilder.withPayload(newPerson).copyHeaders(p.getHeaders()).setHeader("xyz", "hello").build(); - }; - } - - } - - public static class Person { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - return this.name; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof Person && (this.name.equals(((Person) obj).name)); - } - } -} diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationRoutingTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationRoutingTests.java deleted file mode 100644 index 9fb7a4a1e..000000000 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationRoutingTests.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - - -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Sinks; -import reactor.test.StepVerifier; - -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.messaging.handler.DestinationPatternsMessageCondition; -import org.springframework.messaging.rsocket.RSocketRequester; -import org.springframework.util.MimeTypeUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * @author Chris Bono - * @since 3.1 - */ -public class RSocketAutoConfigurationRoutingTests { - @Test - public void testRoutingWithRoute() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.routing-expression=headers.func_name", - "--spring.cloud.function.expected-content-type=text/plain", - "--spring.rsocket.server.port=0"); - ) { - int port = applicationContext.getEnvironment().getProperty("local.rsocket.server.port", Integer.class); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase") - .metadata("{\"func_name\":\"echo\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("hello") - .expectComplete() - .verify(); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("") - .metadata("{\"func_name\":\"echo\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("hello") - .expectComplete() - .verify(); - - rsocketRequesterBuilder.tcp("localhost", port) - .route(RoutingFunction.FUNCTION_NAME) - .metadata("{\"func_name\":\"echo\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("hello") - .expectComplete() - .verify(); - - } - } - - @Test - public void testRoutingWithDefinition() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=uppercase", - "--spring.cloud.function.routing-expression=headers.func_name", - "--spring.cloud.function.expected-content-type=text/plain", - "--spring.rsocket.server.port=0"); - ) { - int port = applicationContext.getEnvironment().getProperty("local.rsocket.server.port", Integer.class); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase") - .metadata("{\"func_name\":\"echo\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("hello") - .expectComplete() - .verify(); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("") - .metadata("{\"func_name\":\"echo\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("hello") - .expectComplete() - .verify(); - - rsocketRequesterBuilder.tcp("localhost", port) - .route(RoutingFunction.FUNCTION_NAME) - .metadata("{\"func_name\":\"echo\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("hello") - .expectComplete() - .verify(); - - } - } - - @Test - public void testRoutingWithDefinitionMessageFunction() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=uppercase", - "--spring.cloud.function.routing-expression=headers.func_name", - "--spring.cloud.function.expected-content-type=text/plain", - "--spring.rsocket.server.port=0"); - ) { - int port = applicationContext.getEnvironment().getProperty("local.rsocket.server.port", Integer.class); - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase") - .metadata("{\"func_name\":\"uppercaseMessage\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - - } - } - - @EnableAutoConfiguration - @Configuration - public static class SampleFunctionConfiguration { - - final Sinks.One consumerData = Sinks.one(); - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - @Bean - public Function, String> uppercaseMessage() { - return msg -> { - assertThat(msg.getHeaders() - .get(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER)).toString().equals("uppercase"); - assertThat(msg.getHeaders() - .get(FunctionRSocketMessageHandler.RECONCILED_LOOKUP_DESTINATION_HEADER)).toString().equals(RoutingFunction.FUNCTION_NAME); - return msg.getPayload().toUpperCase(); - }; - } - - @Bean - public Function concat() { - return v -> v + v; - } - - @Bean - public Function echo() { - return v -> v; - } - - @Bean - public Function, Flux> uppercaseReactive() { - return flux -> flux.map(v -> { - System.out.println("Uppercasing: " + v); - return v.toUpperCase(); - }); - } - - @Bean - public Consumer log() { - return this.consumerData::tryEmitValue; - } - - @Bean - public Supplier source() { - return () -> "test data"; - } - - - } - -} diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationTests.java deleted file mode 100644 index f0b942174..000000000 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationTests.java +++ /dev/null @@ -1,728 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Sinks; -import reactor.test.StepVerifier; - -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.rsocket.context.RSocketServerBootstrap; -import org.springframework.boot.rsocket.server.RSocketServer; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.messaging.rsocket.RSocketRequester; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.MimeType; -import org.springframework.util.MimeTypeUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * @author Chris Bono - * @since 3.1 - */ -public class RSocketAutoConfigurationTests { - - @Test - public void testNonExistingFunctionInRoute() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("foo") - .data("\"hello\"") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectError() - .verify(); - } - } - - @Test - public void testNonExistingFunctionInRouteSingleFunctionInCatalog() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SingleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("blah") - .data("\"hello\"") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("hello") - .expectComplete() - .verify(); - } - } - - - - @Test - public void testImperativeFunctionAsRequestReplyWithDefinition() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=uppercase", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("") - .data("\"hello\"") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - } - } - - @SuppressWarnings("unchecked") - @Test - public void testWithCborContentType() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=uppercase", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - Person p = new Person(); - p.setAge(23); - p.setName("Bob"); - Map m = rsocketRequesterBuilder - .dataMimeType(MimeType.valueOf("application/cbor")) - .tcp("localhost", port) - .route("echoMap") - .data(p) - .retrieveMono(Map.class).block(); - assertThat(m.get("name")).isEqualTo("Bob"); - assertThat(m.get("age")).isEqualTo(23); - } - } - - @Test - @Disabled - public void testImperativeFunctionAsRequestReplyWithDefinitionExplicitExpectedOutputCt() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=uppercase", - "--spring.cloud.function.expected-content-type=application/json", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("") - .data("\"hello\"") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("\"HELLO\"") - .expectComplete() - .verify(); - } - } - - @Test - public void testImperativeFunctionAsRequestReply() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase") - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - } - } - - @Test - public void testWithRouteAndDefinition() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=echo", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase") - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - } - } - - @Test - public void testImperativeFunctionAsRequestReplyWithComposition() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase|concat") - .data("\"hello\"") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLOHELLO") - .expectComplete() - .verify(); - } - } - - @Test - public void testSupplierAsRequestReply() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("source") - .data("\"hello\"") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("test data") - .expectComplete() - .verify(); - } - } - - @Test - public void testImperativeFunctionAsRequestStream() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase") - .data("\"hello\"") - .retrieveFlux(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - } - } - - @Test - public void testImperativeFunctionAsRequestChannel() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase") - //.data(Flux.just("\"Ricky\"", "\"Julien\"", "\"Bubbles\"")) - .data(Flux.just("Ricky", "Julien", "Bubbles")) - .retrieveFlux(String.class) - .as(StepVerifier::create) - .expectNext("RICKY", "JULIEN", "BUBBLES") - .expectComplete() - .verify(); - } - } - - @Test - public void testReactiveFunctionAsRequestReply() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercaseReactive") - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - } - } - - @Test - public void testReactiveFunctionAsRequestStream() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercaseReactive") - .data("hello") - .retrieveFlux(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercaseReactive") - .data("hello") - .retrieveFlux(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercaseReactive") - .data("hello") - .retrieveFlux(String.class) - .as(StepVerifier::create) - .expectNext("HELLO") - .expectComplete() - .verify(); - } - } - - @Test - public void testReactiveFunctionAsRequestChannel() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercaseReactive") - .data(Flux.just("\"Ricky\"", "\"Julien\"", "\"Bubbles\"")) - .retrieveFlux(String.class) - .as(StepVerifier::create) - .expectNext("RICKY", "JULIEN", "BUBBLES") - .expectComplete() - .verify(); - } - } - - @Test - public void testRequestReplyFunctionWithDistributedComposition() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=uppercase|concat", - "--spring.rsocket.server.port=0"); - ) { - int portA = getLocalRsocketPort(applicationContext); - - try ( - ConfigurableApplicationContext applicationContext2 = - new SpringApplicationBuilder(AdditionalFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=reverse>localhost:" + portA + "|wrap", - "--spring.rsocket.server.port=0"); - ) { - - int portB = getLocalRsocketPort(applicationContext2); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext2.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", portB) - .route("reverse>localhost:" + portA + "|wrap") - .data("\"hello\"") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("(OLLEHOLLEH)") - .expectComplete() - .verify(); - } - } - } - - @Disabled("TODO") - @Test - public void testCompositionOverWebSocket() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.REACTIVE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=uppercase|concat", - "--spring.rsocket.server.transport=websocket", - "--spring.rsocket.server.mapping-path=rsockets", - "--server.port=0"); - ) { - ConfigurableEnvironment environment = applicationContext.getEnvironment(); - String httpServerPort = environment.getProperty("local.server.port"); - - try ( - ConfigurableApplicationContext applicationContext2 = - new SpringApplicationBuilder(AdditionalFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.definition=reverse>http://localhost:" + httpServerPort + "/rsockets/uppercase|wrap", - "--spring.rsocket.server.port=0"); - ) { - RSocketServerBootstrap serverBootstrap = applicationContext2.getBean(RSocketServerBootstrap.class); - RSocketServer server = (RSocketServer) ReflectionTestUtils.getField(serverBootstrap, "server"); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext2.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", server.address().getPort()) - .route("reverse") - .data("\"hello\"") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("\"(OLLEHOLLEH)\"") - .expectComplete() - .verify(); - } - } - } - - @Test - public void testFireAndForgetConsumer() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - - SampleFunctionConfiguration config = applicationContext.getBean(SampleFunctionConfiguration.class); - - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route("log") - .data("\"hello\"") - .send() - .as(StepVerifier::create) - .expectComplete() - .verify(); - String result = config.consumerData.asMono().block(); - assertThat(result).isEqualTo("hello"); - } - } - - @Test - public void testRsocketRoutesForAllFunctions() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(AdditionalFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - RSocketServerBootstrap serverBootstrap = applicationContext.getBean(RSocketServerBootstrap.class); - RSocketServer server = (RSocketServer) ReflectionTestUtils.getField(serverBootstrap, "server"); - - RSocketRequester requester = rsocketRequesterBuilder.tcp("localhost", server.address().getPort()); - - requester.route("reverse") - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("olleh") - .expectComplete() - .verify(); - - requester.route("wrap") - .data("\"hello\"") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("(hello)") - .expectComplete() - .verify(); - } - } - - @Test - public void testRoutingWithRoutingFunction() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.cloud.function.routing-expression=headers.function_definition", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - rsocketRequesterBuilder.tcp("localhost", port) - .route(RoutingFunction.FUNCTION_NAME) - .metadata("{\"function_definition\":\"uppercase|concat\"}", MimeTypeUtils.APPLICATION_JSON) - .data("hello") - .retrieveMono(String.class) - .as(StepVerifier::create) - .expectNext("HELLOHELLO") - .expectComplete() - .verify(); - } - } - - @Test - public void testByteArrayInOut() { - try ( - ConfigurableApplicationContext applicationContext = - new SpringApplicationBuilder(SampleFunctionConfiguration.class) - .web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--spring.rsocket.server.port=0"); - ) { - int port = getLocalRsocketPort(applicationContext); - - RSocketRequester.Builder rsocketRequesterBuilder = - applicationContext.getBean(RSocketRequester.Builder.class); - - String result = rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase") - .data("hello".getBytes()) - .retrieveMono(String.class) - .block(); - - assertThat(result).isEqualTo("HELLO"); - - byte[] resultBytes = rsocketRequesterBuilder.tcp("localhost", port) - .route("uppercase") - .data("hello".getBytes()) - .retrieveMono(byte[].class) - .block(); - - assertThat(resultBytes).isEqualTo("HELLO".getBytes()); - } - } - - private int getLocalRsocketPort(ConfigurableApplicationContext context) { - return context.getEnvironment().getProperty("local.rsocket.server.port", Integer.class); - } - - @EnableAutoConfiguration - @Configuration - public static class SampleFunctionConfiguration { - - final Sinks.One consumerData = Sinks.one(); - - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - - @Bean - public Function concat() { - return v -> v + v; - } - - - @Bean - public Function echo() { - return v -> v; - } - - @Bean - public Function, Map> echoMap() { - return v -> v; - } - - @Bean - public Function, Flux> uppercaseReactive() { - return flux -> flux.map(v -> { - System.out.println("Uppercasing: " + v); - return v.toUpperCase(); - }); - } - - @Bean - public Consumer log() { - return this.consumerData::tryEmitValue; - } - - @Bean - public Supplier source() { - return () -> "test data"; - } - - } - - @EnableAutoConfiguration - @Configuration - public static class AdditionalFunctionConfiguration { - - @Bean - public Function reverse() { - return v -> new StringBuilder(v).reverse().toString(); - } - - @Bean - public Function wrap() { - return v -> "(" + v + ")"; - } - - } - - @EnableAutoConfiguration - @Configuration - public static class SingleFunctionConfiguration { - @Bean - public Function echo() { - return v -> v; - } - } - - public static class Person { - private String name; - private int age; - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public int getAge() { - return age; - } - public void setAge(int age) { - this.age = age; - } - } - -} diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RoutingBrokerTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RoutingBrokerTests.java deleted file mode 100644 index e44919ee4..000000000 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RoutingBrokerTests.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.time.Duration; -import java.util.function.Function; - -import io.rsocket.broker.client.spring.BrokerMetadata; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.rsocket.RSocketRequester; - -/** - * @author Spencer Gibb - * @author Oleg Zhurakousky - * @since 3.1 - */ -@Disabled -public class RoutingBrokerTests { - - ConfigurableApplicationContext functionContext; - ConfigurableApplicationContext brokerContext; - ConfigurableApplicationContext clientContext; - - @AfterEach - public void cleanup() { - if (functionContext != null) { - functionContext.close(); - } - if (brokerContext != null) { - brokerContext.close(); - } - if (clientContext != null) { - clientContext.close(); - } - } - - @Test - public void testRoutingWithProperty() throws Exception { - this.setup(true); - RSocketRequester requester = clientContext.getBean(RSocketRequester.class); - // route(uppercase) used to find function, must match io.rsocket.broker.client.address entry - Mono result = requester.route("uppercase") - // auto creates metadata - .data("\"hello\"") - .retrieveMono(String.class); - - StepVerifier - .create(result) - .expectNext("HELLO") - .expectComplete() - .verify(Duration.ofSeconds(15)); - } - - @Test - public void testRoutingWithMessage() throws Exception { - this.setup(false); - RSocketRequester requester = clientContext.getBean(RSocketRequester.class); - BrokerMetadata metadata = clientContext.getBean(BrokerMetadata.class); - Mono result = requester.route("uppercase") // used to find function - .metadata(metadata.address("samplefn")) - .data("\"hello\"") - .retrieveMono(String.class); - - StepVerifier - .create(result) - .expectNext("HELLO") - .expectComplete() - .verify(Duration.ofSeconds(15)); - } - - private void setup(boolean routingWithProperty) { - int brokerProxyPort = TestSocketUtils.findAvailableTcpPort(); - int brokerClusterPort = TestSocketUtils.findAvailableTcpPort(); - // start broker - brokerContext = new SpringApplicationBuilder(SimpleConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.io.rsocket.broker=TRACE", - "--spring.cloud.function.rsocket.enabled=false", - "--io.rsocket.broker.client.enabled=false", - "--io.rsocket.broker.enabled=true", - "--io.rsocket.broker.uri=tcp://localhost:" + brokerProxyPort, - "--io.rsocket.broker.cluster.uri=tcp://localhost:" + brokerClusterPort); - - // start function connecting to broker, service-name=samplefn - functionContext = new SpringApplicationBuilder(SampleFunctionConfiguration.class).web(WebApplicationType.NONE) - .run("--logging.level.org.springframework.cloud.function=DEBUG", - "--logging.level.io.rsocket.broker.client=TRACE", - "--io.rsocket.broker.client.enabled=true", - "--io.rsocket.broker.client.service-name=samplefn", - "--io.rsocket.broker.client.brokers[0]=tcp://localhost:" + brokerProxyPort, - "--io.rsocket.broker.enabled=false", - "--spring.cloud.function.definition=uppercase"); - - // start testclient connecting to broker, for RSocketRequester - clientContext = new SpringApplicationBuilder(SimpleConfiguration.class).web(WebApplicationType.NONE).run( - "--logging.level.io.rsocket.broker.client=TRACE", - "--spring.cloud.function.rsocket.enabled=false", - "--io.rsocket.broker.client.enabled=true", - "--io.rsocket.broker.client.service-name=testclient", - routingWithProperty ? "--io.rsocket.broker.client.address.uppercase.service_name=samplefn" : "", - "--io.rsocket.broker.client.brokers[0]=tcp://localhost:" + brokerProxyPort, - "--io.rsocket.broker.enabled=false"); - } - - - @EnableAutoConfiguration - @Configuration - public static class SimpleConfiguration { - - } - - @EnableAutoConfiguration - @Configuration - public static class SampleFunctionConfiguration { - @Bean - public Function uppercase() { - return v -> v.toUpperCase(); - } - } -} diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/TestSocketUtils.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/TestSocketUtils.java deleted file mode 100644 index 27722015e..000000000 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/TestSocketUtils.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.rsocket; - -import java.net.InetAddress; -import java.net.ServerSocket; -import java.util.Random; - -import javax.net.ServerSocketFactory; - -/** - * Simple test utility to find a random available TCP port. - *

Inspired by the now removed {@code org.springframework.util.SocketUtils} and is only used in a testing capacity. - * - * @author Chris Bono - * @deprecated will soon be removed or consolidated - do not use further - */ -@Deprecated -public final class TestSocketUtils { - - private static final Random random = new Random(System.nanoTime()); - - private TestSocketUtils() { - } - - /** - * Find an available TCP port randomly selected from the range {@code 1024-65535}. - * @return an available TCP port number - * @throws IllegalStateException if no available port could be found - */ - public static int findAvailableTcpPort() { - int minPort = 1024; - int maxPort = 65535; - int portRange = maxPort - minPort; - int candidatePort; - int searchCounter = 0; - do { - if (searchCounter > portRange) { - throw new IllegalStateException(String.format( - "Could not find an available TCP port after %d attempts", searchCounter)); - } - candidatePort = minPort + random.nextInt(portRange + 1); - searchCounter++; - } - while (!isPortAvailable(candidatePort)); - - return candidatePort; - } - - private static boolean isPortAvailable(int port) { - try { - ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket( - port, 1, InetAddress.getByName("localhost")); - serverSocket.close(); - return true; - } - catch (Exception ex) { - return false; - } - } -} diff --git a/spring-cloud-function-rsocket/src/test/resources/application.properties b/spring-cloud-function-rsocket/src/test/resources/application.properties deleted file mode 100644 index 83911652b..000000000 --- a/spring-cloud-function-rsocket/src/test/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -io.rsocket.broker.enabled=false -io.rsocket.broker.client.enabled=false diff --git a/spring-cloud-function-samples/function-functional-sample-aws/.jdk8 b/spring-cloud-function-samples/function-functional-sample-aws/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-samples/function-functional-sample-aws/build.gradle b/spring-cloud-function-samples/function-functional-sample-aws/build.gradle deleted file mode 100644 index a641c315f..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/build.gradle +++ /dev/null @@ -1,93 +0,0 @@ -buildscript { - ext { - springBootVersion = '2.2.0.BUILD-SNAPSHOT' - wrapperVersion = '1.0.17.RELEASE' - shadowVersion = '5.1.0' - } - repositories { - mavenLocal() - jcenter() - mavenCentral() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } - } - dependencies { - classpath "com.github.jengelman.gradle.plugins:shadow:${shadowVersion}" - classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}") - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - classpath("io.spring.gradle:dependency-management-plugin:1.0.8.RELEASE") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'eclipse' -apply plugin: 'com.github.johnrengelman.shadow' -apply plugin: 'org.springframework.boot' -apply plugin: 'org.springframework.boot.experimental.thin-launcher' -apply plugin: 'io.spring.dependency-management' - -group = 'io.spring.sample' -version = '2.0.0.RELEASE' -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -repositories { - mavenLocal() - mavenCentral() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } -} - -ext { - springCloudFunctionVersion = "3.0.0.BUILD-SNAPSHOT" - awsLambdaEventsVersion = "2.0.2" - awsLambdaCoreVersion = "1.1.0" -} -ext['reactor.version'] = "3.1.7.RELEASE" - -assemble.dependsOn = [shadowJar, thinJar] - -jar { - manifest { - attributes 'Main-Class': 'example.Config' - } -} - -import com.github.jengelman.gradle.plugins.shadow.transformers.* - -shadowJar { - classifier = 'aws' - dependencies { - exclude( - dependency("org.springframework.cloud:spring-cloud-function-web:${springCloudFunctionVersion}")) - } - // Required for Spring - mergeServiceFiles() - append 'META-INF/spring.handlers' - append 'META-INF/spring.schemas' - append 'META-INF/spring.tooling' - transform(PropertiesFileTransformer) { - paths = ['META-INF/spring.factories'] - mergeStrategy = "append" - } -} - -configurations { - testCompile.extendsFrom(compileOnly) -} - -dependencyManagement { - imports { - mavenBom "org.springframework.cloud:spring-cloud-function-dependencies:${springCloudFunctionVersion}" - } -} - -dependencies { - compile("org.springframework.cloud:spring-cloud-function-adapter-aws") - compile("org.springframework.cloud:spring-cloud-starter-function-webflux") - compile("org.springframework.boot:spring-boot-configuration-processor") - compileOnly("com.amazonaws:aws-lambda-java-events:${awsLambdaEventsVersion}") - compileOnly("com.amazonaws:aws-lambda-java-core:${awsLambdaCoreVersion}") - testCompile('org.springframework.boot:spring-boot-starter-test') -} diff --git a/spring-cloud-function-samples/function-functional-sample-aws/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-function-samples/function-functional-sample-aws/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ca78035ef..000000000 Binary files a/spring-cloud-function-samples/function-functional-sample-aws/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-functional-sample-aws/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-function-samples/function-functional-sample-aws/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index b33419dee..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip diff --git a/spring-cloud-function-samples/function-functional-sample-aws/gradlew b/spring-cloud-function-samples/function-functional-sample-aws/gradlew deleted file mode 100755 index 27309d923..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/spring-cloud-function-samples/function-functional-sample-aws/gradlew.bat b/spring-cloud-function-samples/function-functional-sample-aws/gradlew.bat deleted file mode 100644 index f6d5974e7..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/spring-cloud-function-samples/function-functional-sample-aws/pom.xml b/spring-cloud-function-samples/function-functional-sample-aws/pom.xml deleted file mode 100644 index 4187df05e..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/pom.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - 4.0.0 - - io.spring.sample - function-functional-sample-aws - 0.0.1-SNAPSHOT - jar - - function-functional-sample-aws - Spring Cloud Function Sample for AWS Lambda functional style - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - UTF-8 - UTF-8 - 1.0.27.RELEASE - 3.9.0 - 4.1.0-SNAPSHOT - - - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - - com.amazonaws - aws-lambda-java-events - ${aws-lambda-events.version} - - - com.amazonaws - aws-lambda-java-core - 1.1.0 - provided - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - false - true - aws - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - diff --git a/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java b/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java deleted file mode 100644 index a237eff87..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java +++ /dev/null @@ -1,37 +0,0 @@ -package example; - -import java.util.function.Function; - -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.support.GenericApplicationContext; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; - -@SpringBootConfiguration -public class FunctionConfiguration implements ApplicationContextInitializer { - - /* - * You need this main method (empty) or explicit example.FunctionConfiguration - * in the POM to ensure boot plug-in makes the correct entry - */ - public static void main(String[] args) { - // empty unless using Custom runtime at which point it should include - // FunctionalSpringApplication.run(FunctionConfiguration.class, args); - } - - @Override - public void initialize(GenericApplicationContext context) { - Function function = (str) -> str + str.toUpperCase(); - - context.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(function).type(FunctionTypeUtils.functionType(String.class, String.class))); - - context.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(new TestFunction()).type(FunctionTypeUtils.functionType(APIGatewayProxyRequestEvent.class, APIGatewayProxyResponseEvent.class))); - - } -} diff --git a/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/TestFunction.java b/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/TestFunction.java deleted file mode 100644 index b097d0ad5..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/TestFunction.java +++ /dev/null @@ -1,13 +0,0 @@ -package example; - -import java.util.function.Function; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; - -public class TestFunction implements Function { - @Override - public APIGatewayProxyResponseEvent apply(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent) { - return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("ok"); - } -} diff --git a/spring-cloud-function-samples/function-functional-sample-aws/src/main/resources/application.properties b/spring-cloud-function-samples/function-functional-sample-aws/src/main/resources/application.properties deleted file mode 100644 index af6da4bf5..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -logging.level.org.springframework.cloud=DEBUG diff --git a/spring-cloud-function-samples/function-functional-sample-aws/src/main/resources/log4j.properties b/spring-cloud-function-samples/function-functional-sample-aws/src/main/resources/log4j.properties deleted file mode 100644 index f644d953c..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/src/main/resources/log4j.properties +++ /dev/null @@ -1,20 +0,0 @@ -log4j.rootCategory=DEBUG, LAMBDA -PID=???? -LOG_LEVEL_PATTERN=%5p -LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] boot%X{context} - ${PID} ${LOG_LEVEL_PATTERN} [%t] --- %c{1}: %m%n -# CONSOLE is set to be a ConsoleAppender using a PatternLayout. -log4j.appender.LAMBDA=com.amazonaws.services.lambda.runtime.log4j.LambdaAppender -log4j.appender.LAMBDA.layout=org.apache.log4j.PatternLayout -log4j.appender.LAMBDA.layout.conversionPattern=${LOG_PATTERN} -log4j.category.org.apache.catalina.startup.DigesterFactory=ERROR -log4j.category.org.apache.catalina.util.LifecycleBase=ERROR -log4j.category.org.apache.coyote.http11.Http11NioProtocol=WARN -log4j.category.org.apache.sshd.common.util.SecurityUtils -log4j.category.org.apache.tomcat.util.net.NioSelectorPool=WARN -log4j.category.org.crsh.plugin=WARN -log4j.category.org.crsh.ssh=WARN -log4j.category.org.eclipse.jetty.util.component.AbstractLifeCycle=ERROR -log4j.category.org.hibernate.validator.internal.util.Version=WARN -log4j.category.org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration=WARN -log4j.category.org.springframework.boot.actuate.endpoint.jmx=WARN -log4j.category.org.thymeleaf=WARN diff --git a/spring-cloud-function-samples/function-functional-sample-aws/src/test/java/example/MapTests.java b/spring-cloud-function-samples/function-functional-sample-aws/src/test/java/example/MapTests.java deleted file mode 100644 index 289a8c9ff..000000000 --- a/spring-cloud-function-samples/function-functional-sample-aws/src/test/java/example/MapTests.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import org.junit.jupiter.api.Test; - -/** - * @author Dave Syer - * - */ -public class MapTests { - - @Test - public void test() { - - } -} diff --git a/spring-cloud-function-samples/function-sample-aws-custom-bean/README.adoc b/spring-cloud-function-samples/function-sample-aws-custom-bean/README.adoc deleted file mode 100644 index fc1fd761f..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom-bean/README.adoc +++ /dev/null @@ -1,12 +0,0 @@ -This sample uses the custom runtime type on AWS lambda using @Bean style configuration. -However, changing configuration to functional bean registration is supported as well and shown in `function-sample-aws-custom` example. - -To run the app in AWS choose the "Custom Runtime" runtime type, and upload the -.zip file that gets built on the command line with `mvn package` (look -in `target`). -There are several functions defined in the `com.example.LambdaApplication`, so identify the selected function in "Handler" -You can also use function composition (e.g., `uppercase|reverse`) - -You can test any function in this example with any String as input, but the Lambda UI only allows valid JSON as -test data, so you will have to escape the input with double quotes. - diff --git a/spring-cloud-function-samples/function-sample-aws-custom-bean/pom.xml b/spring-cloud-function-samples/function-sample-aws-custom-bean/pom.xml deleted file mode 100644 index 6a54471cd..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom-bean/pom.xml +++ /dev/null @@ -1,142 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - io.spring.sample - function-sample-aws-custom-bean - 0.0.1-SNAPSHOT - AWS Custom Runtime - @Bean sample - Demo project for Spring Cloud Function with custom AWS Lambda runtime using @Bean style - - - 1.0.27.RELEASE - 4.1.0-SNAPSHOT - - - - - - com.amazonaws - aws-lambda-java-events - 3.9.0 - - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - - org.slf4j - slf4j-jdk14 - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - maven-surefire-plugin - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - maven-assembly-plugin - - - zip - package - - single - - false - - - - - src/assembly/zip.xml - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/assembly/zip.xml b/spring-cloud-function-samples/function-sample-aws-custom-bean/src/assembly/zip.xml deleted file mode 100644 index 62e8b951e..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/assembly/zip.xml +++ /dev/null @@ -1,35 +0,0 @@ - - zip - - zip - - - - - target/classes - / - true - - bootstrap - - - - target/classes - / - true - 0775 - - bootstrap - - - - - - /lib - false - runtime - - - \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/java/com/example/LambdaApplication.java b/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/java/com/example/LambdaApplication.java deleted file mode 100644 index 7fb2ecec8..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/java/com/example/LambdaApplication.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.example; - -import java.util.Arrays; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; -import org.springframework.util.ObjectUtils; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; - -@SpringBootApplication -public class LambdaApplication { - - private static Log logger = LogFactory.getLog(LambdaApplication.class); - - @Bean - public Consumer consume() { - return value -> { - logger.info("Consuming: " + value); - }; - } - - @Bean - public Function uppercase() { - return value -> { - logger.info("UPPERCASING: " + value); - return value.toUpperCase(); - }; - } - - @Bean - public Function extractPayloadFromGatewayEvent() { - return value -> { - logger.info("ECHO Payload from Gateway Event: " + value.getBody()); - return value.getBody(); - }; - } - - @Bean - public Function, Message> echoMessage() { - return value -> { - logger.info("ECHO MESSAGE: " + value); - return value; - }; - } - - @Bean - public Function reverse() { - return value -> { - logger.info("REVERSING: " + value); - return new StringBuilder(value).reverse().toString(); - }; - } - - - - public static void main(String[] args) { - logger.info("==> Starting: LambdaApplication"); - if (!ObjectUtils.isEmpty(args)) { - logger.info("==> args: " + Arrays.asList(args)); - } - SpringApplication.run(LambdaApplication.class, args); - } - -} diff --git a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/resources/application.properties deleted file mode 100644 index dbb9aec79..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -spring.main.web-application-type=none -logging.level.org.springframework.cloud=DEBUG \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/resources/bootstrap b/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/resources/bootstrap deleted file mode 100755 index ab39ddb01..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/resources/bootstrap +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -cd ${LAMBDA_TASK_ROOT:-.} - -java -Dspring.main.web-application-type=none -Dspring.jmx.enabled=false \ - -noverify -XX:TieredStopAtLevel=1 -Xss256K -XX:MaxMetaspaceSize=128M \ - -Djava.security.egd=file:/dev/./urandom \ - -cp .:`echo lib/*.jar | tr ' ' :` com.example.LambdaApplication diff --git a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/test/resources/testBootstrap b/spring-cloud-function-samples/function-sample-aws-custom-bean/src/test/resources/testBootstrap deleted file mode 100755 index 142d4caad..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/test/resources/testBootstrap +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -while true -do - sleep 1 -done \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-custom/README.adoc b/spring-cloud-function-samples/function-sample-aws-custom/README.adoc deleted file mode 100644 index a0948a6ed..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom/README.adoc +++ /dev/null @@ -1,11 +0,0 @@ -This sample uses the custom runtime type on AWS lambda using function bean registration style configuration. -However, changing configuration to @Bean registration is supported as well and shown in `function-sample-aws-custom-bean` example. - -To run the app in AWS choose the "Custom Runtime" runtime type, and upload the -.zip file that gets built on the command line with `mvn package` (look -in `target`). -There is a single function defined in the `com.example.LambdaApplication` - `uppercase` which you would typically -identified as "Handler", but since it's the only one any value would do, so keeping default "hello.handler" is fine. - -You can test it with any String as input, but the Lambda UI only allows valid JSON as -test data, so you will have to escape the input with double quotes. diff --git a/spring-cloud-function-samples/function-sample-aws-custom/pom.xml b/spring-cloud-function-samples/function-sample-aws-custom/pom.xml deleted file mode 100644 index 4bde5f60b..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom/pom.xml +++ /dev/null @@ -1,192 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - io.spring.sample - function-sample-aws-custom - 0.0.1-SNAPSHOT - function-sample-aws-custom - Demo project for Spring Cloud Function with custom AWS Lambda runtime - - - 1.0.27.RELEASE - 4.1.0-SNAPSHOT - - - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - - org.springframework.cloud - spring-cloud-function-web - test - - - org.springframework.boot - spring-boot-starter-web - - - jakarta.servlet - jakarta.servlet-api - provided - - - org.springframework.boot - spring-boot-starter - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - - - maven-surefire-plugin - - - com/example/ContainerTests.java - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - maven-assembly-plugin - - - zip - package - - single - - false - - - - - src/assembly/zip.xml - - - - - - - - - integration - - - - maven-surefire-plugin - 2.22.0 - - - integration-test - - test - - - - none - - - com/example/ContainerTests.java - - - - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/assembly/zip.xml b/spring-cloud-function-samples/function-sample-aws-custom/src/assembly/zip.xml deleted file mode 100644 index 62e8b951e..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/assembly/zip.xml +++ /dev/null @@ -1,35 +0,0 @@ - - zip - - zip - - - - - target/classes - / - true - - bootstrap - - - - target/classes - / - true - 0775 - - bootstrap - - - - - - /lib - false - runtime - - - \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java b/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java deleted file mode 100644 index da9d85523..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.example; - -import java.util.function.Function; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionalSpringApplication; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.support.GenericApplicationContext; - -@SpringBootApplication -public class LambdaApplication - implements ApplicationContextInitializer { - - private static Log logger = LogFactory.getLog(LambdaApplication.class); - - public Function uppercase() { - return value -> { - logger.info("Processing: " + value); - if (value.equals("error")) { - throw new IllegalArgumentException("Intentional"); - } - return value.toUpperCase(); - }; - } - - public static void main(String[] args) { - FunctionalSpringApplication.run(LambdaApplication.class, args); - } - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(uppercase()).type(FunctionTypeUtils.functionType(String.class, String.class))); - } -} diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/application.properties deleted file mode 100644 index 48d1c32ec..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/application.properties +++ /dev/null @@ -1,4 +0,0 @@ -#spring.cloud.function.web.export.enabled=true -#spring.cloud.function.web.export.debug=true -spring.main.web-application-type=none -logging.level.org.springframework.cloud=DEBUG \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/bootstrap b/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/bootstrap deleted file mode 100755 index ab39ddb01..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/bootstrap +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -cd ${LAMBDA_TASK_ROOT:-.} - -java -Dspring.main.web-application-type=none -Dspring.jmx.enabled=false \ - -noverify -XX:TieredStopAtLevel=1 -Xss256K -XX:MaxMetaspaceSize=128M \ - -Djava.security.egd=file:/dev/./urandom \ - -cp .:`echo lib/*.jar | tr ' ' :` com.example.LambdaApplication diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/LambdaApplicationTests.java b/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/LambdaApplicationTests.java deleted file mode 100644 index b7e560f00..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/LambdaApplicationTests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.example; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.cloud.function.adapter.test.aws.AWSCustomRuntime; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; - - -/** - * @author Oleg Zhurakousky - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"spring.main.web-application-type=servlet"}) -@ContextConfiguration(classes = {AWSCustomRuntime.class}, initializers = LambdaApplication.class) -@TestPropertySource(properties = {"_HANDLER=uppercase"}) -public class LambdaApplicationTests { - @Autowired - private AWSCustomRuntime aws; - - @Test - void testWithCustomRuntime() throws Exception { - assertThat(aws.exchange("\"oleg\"").getPayload()).isEqualTo("\"OLEG\""); - assertThat(aws.exchange("\"dave\"").getPayload()).isEqualTo("\"DAVE\""); - } -} diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/test/resources/testBootstrap b/spring-cloud-function-samples/function-sample-aws-custom/src/test/resources/testBootstrap deleted file mode 100755 index 142d4caad..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/test/resources/testBootstrap +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -while true -do - sleep 1 -done \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-native/.gitignore b/spring-cloud-function-samples/function-sample-aws-native/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-aws-native/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-aws-native/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index c1dd12f17..000000000 Binary files a/spring-cloud-function-samples/function-sample-aws-native/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-aws-native/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-aws-native/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index b74bf7fcd..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/spring-cloud-function-samples/function-sample-aws-native/Dockerfile b/spring-cloud-function-samples/function-sample-aws-native/Dockerfile deleted file mode 100644 index 170191389..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -FROM arm64v8/amazonlinux:2 - -RUN yum -y update \ - && yum install -y tar unzip gzip bzip2-devel ed gcc gcc-c++ gcc-gfortran \ - less libcurl-devel openssl openssl-devel readline-devel xz-devel \ - zlib-devel glibc-static libcxx libcxx-devel llvm-toolset-7 zlib-static \ - && rm -rf /var/cache/yum - -ENV GRAAL_VERSION 22.3.1 -ENV GRAAL_FOLDERNAME graalvm-ce-java19-${GRAAL_VERSION} -ENV GRAAL_FILENAME graalvm-ce-java19-linux-aarch64-${GRAAL_VERSION}.tar.gz -RUN curl -4 -L https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-${GRAAL_VERSION}/${GRAAL_FILENAME} | tar -xvz -RUN mv $GRAAL_FOLDERNAME /usr/lib/graalvm -RUN rm -rf $GRAAL_FOLDERNAME - -# Graal maven plugin requires Maven 3.3.x -ENV MVN_VERSION 3.6.3 -ENV MVN_FOLDERNAME apache-maven-${MVN_VERSION} -ENV MVN_FILENAME apache-maven-${MVN_VERSION}-bin.tar.gz -RUN curl -4 -L https://mirrors.ukfast.co.uk/sites/ftp.apache.org/maven/maven-3/${MVN_VERSION}/binaries/${MVN_FILENAME} | tar -xvz -RUN mv $MVN_FOLDERNAME /usr/lib/maven -RUN rm -rf $MVN_FOLDERNAME - -# Gradle -ENV GRADLE_VERSION 7.4.1 -ENV GRADLE_FOLDERNAME gradle-${GRADLE_VERSION} -ENV GRADLE_FILENAME gradle-${GRADLE_VERSION}-bin.zip -RUN curl -LO https://downloads.gradle-dn.com/distributions/gradle-${GRADLE_VERSION}-bin.zip -RUN unzip gradle-${GRADLE_VERSION}-bin.zip -RUN mv $GRADLE_FOLDERNAME /usr/lib/gradle -RUN rm -rf $GRADLE_FOLDERNAME - -VOLUME /project -WORKDIR /project - -RUN /usr/lib/graalvm/bin/gu install native-image -RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image -RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn -RUN ln -s /usr/lib/gradle/bin/gradle /usr/bin/gradle - -ENV JAVA_HOME /usr/lib/graalvm - -WORKDIR /function-sample-aws-native diff --git a/spring-cloud-function-samples/function-sample-aws-native/README.md b/spring-cloud-function-samples/function-sample-aws-native/README.md deleted file mode 100644 index 5a4e5a0a6..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Introduction - -This example shows GraaalVM native spring-cloud-function application. The application itself is very simple and contains two functions - `uppercase` & `lowercase`. -Unless specific value is specified as `handler`, the application will fall back on `RoutingFunction` where you can pass the routing instruction -via `spring.cloud.function.definition` Message header. If using API Gateway you can pass such header as HTTP header. - -# To run - -## If you are on OSX Apple M1 Pro (arch64) - -You first need to build a Docker image where you will actually build project. -To do that execute the following command form [project directory - -``` -$ docker build -t "al2-graalvm19:native-uppercase" . -``` -Start the container - -``` -$ docker run -dit -v `pwd`:`pwd` -w `pwd` -v ~/.m2:/root/.m2 al2-graalvm19:native-uppercase - ``` - -Now navigate to the image terminal. Your working directory is alredy set for the root of the project. You can verify it by executing `ls`. - -Build the project: - -``` -./mvnw clean -Pnative native:compile -DskipTests -``` - -Once the build finishes, you can deploy it. - -## Deploying to AWS LAmbda - -Start *AWS Dashboard* and navigate to **AWS Lambda** Services - -Click on `Create Function`. Enter `uppercase` for *function name*. For the runtime select `Provide your own bootstrap on Amazon Linux 2`. -Make sure you select the proper architecture (`x86_64` or `arm64`). - -Click on `Create Function` again. - -Next you need to upload your project, so click on `Upload From` and point to the ZIP file that was created by the build process (in the `target` directory). - -Once the file is uploaded navigate to the `Test` tab. You can change the input data or use the default. Basically you need to pas a String in a JSON format such as `"hello"` and you should see the output `"HELLO"`. diff --git a/spring-cloud-function-samples/function-sample-aws-native/mvnw b/spring-cloud-function-samples/function-sample-aws-native/mvnw deleted file mode 100755 index 8a8fb2282..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/mvnw +++ /dev/null @@ -1,316 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`\\unset -f command; \\command -v java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-aws-native/mvnw.cmd b/spring-cloud-function-samples/function-sample-aws-native/mvnw.cmd deleted file mode 100644 index 1d8ab018e..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/mvnw.cmd +++ /dev/null @@ -1,188 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-aws-native/pom.xml b/spring-cloud-function-samples/function-sample-aws-native/pom.xml deleted file mode 100644 index ca936d5d7..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/pom.xml +++ /dev/null @@ -1,187 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - oz.native.sample - function-sample-aws-native - 0.0.1-SNAPSHOT - function-sample-aws-native - Sample of AWS with Spring Native - - 17 - 2023.0.0-SNAPSHOT - - - - org.springframework.boot - spring-boot-starter - - - org.springframework.cloud - spring-cloud-function-context - - - org.springframework.cloud - spring-cloud-function-web - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - - com.amazonaws - aws-lambda-java-events - 3.9.0 - - - com.amazonaws - aws-lambda-java-core - 1.1.0 - provided - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud.version} - pom - import - - - - - - - native - - - - org.graalvm.buildtools - native-maven-plugin - - - - --enable-url-protocols=http - - - - - - build - - package - - - test - - test - - test - - - - - maven-assembly-plugin - - - native-zip - package - - single - - false - - - - - src/assembly/native.xml - - - - - - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - maven-assembly-plugin - - - java-zip - package - - single - - false - - - - - src/assembly/java.xml - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - diff --git a/spring-cloud-function-samples/function-sample-aws-native/src/assembly/java.xml b/spring-cloud-function-samples/function-sample-aws-native/src/assembly/java.xml deleted file mode 100644 index bd4961b58..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/src/assembly/java.xml +++ /dev/null @@ -1,31 +0,0 @@ - - java-zip - - zip - - - - - target/classes - / - - - src/shell/java - / - true - 0775 - - bootstrap - - - - - - /lib - false - runtime - - - \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-native/src/assembly/native.xml b/spring-cloud-function-samples/function-sample-aws-native/src/assembly/native.xml deleted file mode 100644 index 805947c52..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/src/assembly/native.xml +++ /dev/null @@ -1,29 +0,0 @@ - - native-zip - - zip - - - - - src/shell/native - / - true - 0775 - - bootstrap - - - - target - / - true - 0775 - - function-sample-aws-native - - - - \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-native/src/main/java/com/example/demo/NativeUppercaseApplication.java b/spring-cloud-function-samples/function-sample-aws-native/src/main/java/com/example/demo/NativeUppercaseApplication.java deleted file mode 100644 index cb3f3adcb..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/src/main/java/com/example/demo/NativeUppercaseApplication.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.example.demo; - -import java.util.function.Function; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.context.DefaultMessageRoutingHandler; -import org.springframework.cloud.function.context.MessageRoutingCallback; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; - -@SpringBootApplication -public class NativeUppercaseApplication { - - Log logger = LogFactory.getLog(NativeUppercaseApplication.class); - - public static void main(String[] args) { - SpringApplication.run(NativeUppercaseApplication.class, args); - } - - @Bean - public MessageRoutingCallback customRouter() { - return new MessageRoutingCallback() { - @Override - public String routingResult(Message message) { - logger.info("Received message: " + message); - return (String) message.getHeaders().get("spring.cloud.function.definition"); - } - }; - } - - @Bean - public Function uppercase() { - return v -> { - System.out.println("Uppercasing " + v); - return v.toUpperCase(); - }; - } - - @Bean - public Function lowercase() { - return v -> { - System.out.println("Lowercasing " + v); - return v.toUpperCase(); - }; - } - -} diff --git a/spring-cloud-function-samples/function-sample-aws-native/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-aws-native/src/main/resources/application.properties deleted file mode 100644 index 8b1378917..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring-cloud-function-samples/function-sample-aws-native/src/shell/java/bootstrap b/spring-cloud-function-samples/function-sample-aws-native/src/shell/java/bootstrap deleted file mode 100644 index aada735b0..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/src/shell/java/bootstrap +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -cd ${LAMBDA_TASK_ROOT:-.} - -java -Dspring.main.web-application-type=none -Dlogging.level.org.springframework=DEBUG \ - -noverify -XX:TieredStopAtLevel=1 -Xss256K -XX:MaxMetaspaceSize=128M \ - -cp .:`echo lib/*.jar | tr ' ' :` com.example.demo.NativeUppercaseApplication \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-native/src/shell/native/bootstrap b/spring-cloud-function-samples/function-sample-aws-native/src/shell/native/bootstrap deleted file mode 100644 index 356bae8ce..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/src/shell/native/bootstrap +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -cd ${LAMBDA_TASK_ROOT:-.} - -./function-sample-aws-native -Dlogging.level.org.springframework=DEBUG diff --git a/spring-cloud-function-samples/function-sample-aws-native/src/test/java/com/example/demo/NativeUppercaseApplicationTests.java b/spring-cloud-function-samples/function-sample-aws-native/src/test/java/com/example/demo/NativeUppercaseApplicationTests.java deleted file mode 100644 index a6f2d1e13..000000000 --- a/spring-cloud-function-samples/function-sample-aws-native/src/test/java/com/example/demo/NativeUppercaseApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.demo; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class NativeUppercaseApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/spring-cloud-function-samples/function-sample-aws-routing/.jdk8 b/spring-cloud-function-samples/function-sample-aws-routing/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-samples/function-sample-aws-routing/README.adoc b/spring-cloud-function-samples/function-sample-aws-routing/README.adoc deleted file mode 100644 index 6d0c545a7..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/README.adoc +++ /dev/null @@ -1,34 +0,0 @@ -This example demonstrates routing capabilities of spring-cloud-function when deployed as AWS Lambdas. - -Usually when you deploy configuration (non-custom runtime) with a single function it is automatically recognized and bound as AWS Lambda -by `org.springframework.cloud.function.adapter.aws.FunctionInvoker` (FunctionInvoker). - -However when you have multiple functions present in your configuration you need to tell `FunctionInvoker` the target function definition. - -You can do so in two different ways. - -1. You can provide `spring_cloud_function_definition` environment variable setting its value to the desired function definition, which could also be composition -(e.g., `spring_cloud_function_definition=foo|bar`). - -NOTE: Keep in mind though that since AWS does not allow dots `.` and/or hyphens`-` in the name of the environment variable, you can benefit from boot support and simply substitute -dots with underscores and hyphens with camel case. So for example `spring.cloud.function.definition` becomes `spring_cloud_function_definition` -and `spring.cloud.function.routing-expression` becomes `spring_cloud_function_routingExpression`. - -2. A more dynamic and recommended approach would be to fallback on auto routing capabilities of spring-cloud function's in AWS environment. -Basically every time you have more then one function in your configuration, the framework will bind -[Routing Function](https://docs.spring.io/spring-cloud-function/docs/3.1.3/reference/html/spring-cloud-function.html#_function_routing_and_filtering) -as AWS Lambda, and all you need to to is provide a routing instruction via Message headers or environment variables. The instructions could themselves be very dynamic, since we support both SpEL and registering a callback interface. For more details on routing mechanisms please refer to -[Function Routing and Filtering](https://docs.spring.io/spring-cloud-function/docs/3.1.3/reference/html/spring-cloud-function.html#_function_routing_and_filtering) section. - - -In this example we have configuration with two functions; `uppercase` and `reverse`. -When executing from AWS Lambda functions dashboard you can simply provide one of the mentioned properties as environment variables via Configuration tab. -For example, you can set `spring_cloud_function_routingExpression` environment variable with the value of literal; SpEL expression `'uppercase'` (not the single quotes). - -As for API Gateway, you can also pass routing instructions as Message headers by proving them as HTTP headers. -You can test it with API Gateway dashboard or (once deployed), you can for example POST to it via `curl`. -Here is the example of curl command - -``` -curl -X POST https://[. . .].execute-api.eu-west-3.amazonaws.com/route/aws-routing-gw -H "spring.cloud.function.definition: uppercase" -H "Content-Type: application/json" -d '"foo"' -``` \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-routing/build.gradle b/spring-cloud-function-samples/function-sample-aws-routing/build.gradle deleted file mode 100644 index a641c315f..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/build.gradle +++ /dev/null @@ -1,93 +0,0 @@ -buildscript { - ext { - springBootVersion = '2.2.0.BUILD-SNAPSHOT' - wrapperVersion = '1.0.17.RELEASE' - shadowVersion = '5.1.0' - } - repositories { - mavenLocal() - jcenter() - mavenCentral() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } - } - dependencies { - classpath "com.github.jengelman.gradle.plugins:shadow:${shadowVersion}" - classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}") - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - classpath("io.spring.gradle:dependency-management-plugin:1.0.8.RELEASE") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'eclipse' -apply plugin: 'com.github.johnrengelman.shadow' -apply plugin: 'org.springframework.boot' -apply plugin: 'org.springframework.boot.experimental.thin-launcher' -apply plugin: 'io.spring.dependency-management' - -group = 'io.spring.sample' -version = '2.0.0.RELEASE' -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -repositories { - mavenLocal() - mavenCentral() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } -} - -ext { - springCloudFunctionVersion = "3.0.0.BUILD-SNAPSHOT" - awsLambdaEventsVersion = "2.0.2" - awsLambdaCoreVersion = "1.1.0" -} -ext['reactor.version'] = "3.1.7.RELEASE" - -assemble.dependsOn = [shadowJar, thinJar] - -jar { - manifest { - attributes 'Main-Class': 'example.Config' - } -} - -import com.github.jengelman.gradle.plugins.shadow.transformers.* - -shadowJar { - classifier = 'aws' - dependencies { - exclude( - dependency("org.springframework.cloud:spring-cloud-function-web:${springCloudFunctionVersion}")) - } - // Required for Spring - mergeServiceFiles() - append 'META-INF/spring.handlers' - append 'META-INF/spring.schemas' - append 'META-INF/spring.tooling' - transform(PropertiesFileTransformer) { - paths = ['META-INF/spring.factories'] - mergeStrategy = "append" - } -} - -configurations { - testCompile.extendsFrom(compileOnly) -} - -dependencyManagement { - imports { - mavenBom "org.springframework.cloud:spring-cloud-function-dependencies:${springCloudFunctionVersion}" - } -} - -dependencies { - compile("org.springframework.cloud:spring-cloud-function-adapter-aws") - compile("org.springframework.cloud:spring-cloud-starter-function-webflux") - compile("org.springframework.boot:spring-boot-configuration-processor") - compileOnly("com.amazonaws:aws-lambda-java-events:${awsLambdaEventsVersion}") - compileOnly("com.amazonaws:aws-lambda-java-core:${awsLambdaCoreVersion}") - testCompile('org.springframework.boot:spring-boot-starter-test') -} diff --git a/spring-cloud-function-samples/function-sample-aws-routing/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-function-samples/function-sample-aws-routing/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ca78035ef..000000000 Binary files a/spring-cloud-function-samples/function-sample-aws-routing/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-aws-routing/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-function-samples/function-sample-aws-routing/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index b33419dee..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip diff --git a/spring-cloud-function-samples/function-sample-aws-routing/gradlew b/spring-cloud-function-samples/function-sample-aws-routing/gradlew deleted file mode 100755 index 27309d923..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/spring-cloud-function-samples/function-sample-aws-routing/gradlew.bat b/spring-cloud-function-samples/function-sample-aws-routing/gradlew.bat deleted file mode 100644 index f6d5974e7..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/spring-cloud-function-samples/function-sample-aws-routing/pom.xml b/spring-cloud-function-samples/function-sample-aws-routing/pom.xml deleted file mode 100644 index c9a21c2d4..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/pom.xml +++ /dev/null @@ -1,145 +0,0 @@ - - - 4.0.0 - - io.spring.sample - function-sample-aws-routing - 0.0.1-SNAPSHOT - jar - - function-sample-aws-routing - Spring Cloud Function Sample for AWS Lambda - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - UTF-8 - UTF-8 - 1.0.27.RELEASE - 2.0.2 - 4.1.0-SNAPSHOT - - - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - - org.springframework.boot - spring-boot-starter-web - - - - com.amazonaws - aws-lambda-java-events - ${aws-lambda-events.version} - - - com.amazonaws - aws-lambda-java-core - 1.1.0 - provided - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - false - true - aws - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample-aws-routing/src/main/java/example/FunctionConfiguration.java b/spring-cloud-function-samples/function-sample-aws-routing/src/main/java/example/FunctionConfiguration.java deleted file mode 100644 index 29ccdf0d5..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/src/main/java/example/FunctionConfiguration.java +++ /dev/null @@ -1,29 +0,0 @@ -package example; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class FunctionConfiguration { - - /* - * You need this main method or explicit example.FunctionConfiguration - * in the POM to ensure boot plug-in makes the correct entry - */ - public static void main(String[] args) { - SpringApplication.run(FunctionConfiguration.class, args); - } - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } - - @Bean - public Function reverse() { - return value -> new StringBuilder(value).reverse().toString(); - } -} diff --git a/spring-cloud-function-samples/function-sample-aws-routing/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-aws-routing/src/main/resources/application.properties deleted file mode 100644 index af6da4bf5..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -logging.level.org.springframework.cloud=DEBUG diff --git a/spring-cloud-function-samples/function-sample-aws-routing/src/main/resources/log4j.properties b/spring-cloud-function-samples/function-sample-aws-routing/src/main/resources/log4j.properties deleted file mode 100644 index f644d953c..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/src/main/resources/log4j.properties +++ /dev/null @@ -1,20 +0,0 @@ -log4j.rootCategory=DEBUG, LAMBDA -PID=???? -LOG_LEVEL_PATTERN=%5p -LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] boot%X{context} - ${PID} ${LOG_LEVEL_PATTERN} [%t] --- %c{1}: %m%n -# CONSOLE is set to be a ConsoleAppender using a PatternLayout. -log4j.appender.LAMBDA=com.amazonaws.services.lambda.runtime.log4j.LambdaAppender -log4j.appender.LAMBDA.layout=org.apache.log4j.PatternLayout -log4j.appender.LAMBDA.layout.conversionPattern=${LOG_PATTERN} -log4j.category.org.apache.catalina.startup.DigesterFactory=ERROR -log4j.category.org.apache.catalina.util.LifecycleBase=ERROR -log4j.category.org.apache.coyote.http11.Http11NioProtocol=WARN -log4j.category.org.apache.sshd.common.util.SecurityUtils -log4j.category.org.apache.tomcat.util.net.NioSelectorPool=WARN -log4j.category.org.crsh.plugin=WARN -log4j.category.org.crsh.ssh=WARN -log4j.category.org.eclipse.jetty.util.component.AbstractLifeCycle=ERROR -log4j.category.org.hibernate.validator.internal.util.Version=WARN -log4j.category.org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration=WARN -log4j.category.org.springframework.boot.actuate.endpoint.jmx=WARN -log4j.category.org.thymeleaf=WARN diff --git a/spring-cloud-function-samples/function-sample-aws-routing/src/test/java/example/MapTests.java b/spring-cloud-function-samples/function-sample-aws-routing/src/test/java/example/MapTests.java deleted file mode 100644 index 289a8c9ff..000000000 --- a/spring-cloud-function-samples/function-sample-aws-routing/src/test/java/example/MapTests.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import org.junit.jupiter.api.Test; - -/** - * @author Dave Syer - * - */ -public class MapTests { - - @Test - public void test() { - - } -} diff --git a/spring-cloud-function-samples/function-sample-aws/.jdk8 b/spring-cloud-function-samples/function-sample-aws/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-samples/function-sample-aws/build.gradle b/spring-cloud-function-samples/function-sample-aws/build.gradle deleted file mode 100644 index 60ca5c3bb..000000000 --- a/spring-cloud-function-samples/function-sample-aws/build.gradle +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - */ - -plugins { - id 'java-library' - id 'maven-publish' -} - -repositories { - mavenLocal() - maven { - url = uri('https://repo.spring.io/snapshot') - } - - maven { - url = uri('https://repo.spring.io/milestone') - } - - maven { - url = uri('https://repo.maven.apache.org/maven2/') - } -} - -dependencies { - api 'org.springframework.cloud:spring-cloud-function-adapter-aws:4.0.3-SNAPSHOT' - api 'com.amazonaws:aws-lambda-java-events:3.9.0' - api 'org.springframework.boot:spring-boot-configuration-processor:3.0.5' - testImplementation 'org.springframework.boot:spring-boot-starter-test:3.0.5' - compileOnly 'com.amazonaws:aws-lambda-java-core:1.1.0' -} - -group = 'io.spring.sample' -version = '4.0.0.RELEASE' -description = 'function-sample-aws' -java.sourceCompatibility = JavaVersion.VERSION_1_8 - -publishing { - publications { - maven(MavenPublication) { - from(components.java) - } - } -} - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} - -tasks.withType(Javadoc) { - options.encoding = 'UTF-8' -} diff --git a/spring-cloud-function-samples/function-sample-aws/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-function-samples/function-sample-aws/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index c1962a79e..000000000 Binary files a/spring-cloud-function-samples/function-sample-aws/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-aws/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-function-samples/function-sample-aws/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 37aef8d3f..000000000 --- a/spring-cloud-function-samples/function-sample-aws/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip -networkTimeout=10000 -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/spring-cloud-function-samples/function-sample-aws/gradlew b/spring-cloud-function-samples/function-sample-aws/gradlew deleted file mode 100755 index aeb74cbb4..000000000 --- a/spring-cloud-function-samples/function-sample-aws/gradlew +++ /dev/null @@ -1,245 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/spring-cloud-function-samples/function-sample-aws/gradlew.bat b/spring-cloud-function-samples/function-sample-aws/gradlew.bat deleted file mode 100644 index 6689b85be..000000000 --- a/spring-cloud-function-samples/function-sample-aws/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/spring-cloud-function-samples/function-sample-aws/pom.xml b/spring-cloud-function-samples/function-sample-aws/pom.xml deleted file mode 100644 index dfb67f6ea..000000000 --- a/spring-cloud-function-samples/function-sample-aws/pom.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - 4.0.0 - - io.spring.sample - function-sample-aws - 0.0.1-SNAPSHOT - jar - - function-sample-aws - Spring Cloud Function Sample for AWS Lambda - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - UTF-8 - UTF-8 - 1.0.29.RELEASE - 3.9.0 - 4.1.0-SNAPSHOT - - - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - - com.amazonaws - aws-lambda-java-events - ${aws-lambda-events.version} - - - com.amazonaws - aws-lambda-java-core - 1.1.0 - provided - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - org.apache.maven.plugins - maven-shade-plugin - - false - true - aws - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample-aws/src/main/java/example/FunctionConfiguration.java b/spring-cloud-function-samples/function-sample-aws/src/main/java/example/FunctionConfiguration.java deleted file mode 100644 index ade13c608..000000000 --- a/spring-cloud-function-samples/function-sample-aws/src/main/java/example/FunctionConfiguration.java +++ /dev/null @@ -1,31 +0,0 @@ -package example; - -import java.util.function.Function; - -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class FunctionConfiguration { - - /* - * You need this main method (empty) or explicit example.FunctionConfiguration - * in the POM to ensure boot plug-in makes the correct entry - */ - public static void main(String[] args) { - // empty unless using Custom runtime at which point it should include - // SpringApplication.run(FunctionConfiguration.class, args); - } - - @Bean - public Function uppercase() { - return value -> { - if (value.equals("exception")) { - throw new RuntimeException("Intentional exception"); - } - else { - return value.toUpperCase(); - } - }; - } -} diff --git a/spring-cloud-function-samples/function-sample-aws/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-aws/src/main/resources/application.properties deleted file mode 100644 index af6da4bf5..000000000 --- a/spring-cloud-function-samples/function-sample-aws/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -logging.level.org.springframework.cloud=DEBUG diff --git a/spring-cloud-function-samples/function-sample-aws/src/main/resources/log4j.properties b/spring-cloud-function-samples/function-sample-aws/src/main/resources/log4j.properties deleted file mode 100644 index f644d953c..000000000 --- a/spring-cloud-function-samples/function-sample-aws/src/main/resources/log4j.properties +++ /dev/null @@ -1,20 +0,0 @@ -log4j.rootCategory=DEBUG, LAMBDA -PID=???? -LOG_LEVEL_PATTERN=%5p -LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] boot%X{context} - ${PID} ${LOG_LEVEL_PATTERN} [%t] --- %c{1}: %m%n -# CONSOLE is set to be a ConsoleAppender using a PatternLayout. -log4j.appender.LAMBDA=com.amazonaws.services.lambda.runtime.log4j.LambdaAppender -log4j.appender.LAMBDA.layout=org.apache.log4j.PatternLayout -log4j.appender.LAMBDA.layout.conversionPattern=${LOG_PATTERN} -log4j.category.org.apache.catalina.startup.DigesterFactory=ERROR -log4j.category.org.apache.catalina.util.LifecycleBase=ERROR -log4j.category.org.apache.coyote.http11.Http11NioProtocol=WARN -log4j.category.org.apache.sshd.common.util.SecurityUtils -log4j.category.org.apache.tomcat.util.net.NioSelectorPool=WARN -log4j.category.org.crsh.plugin=WARN -log4j.category.org.crsh.ssh=WARN -log4j.category.org.eclipse.jetty.util.component.AbstractLifeCycle=ERROR -log4j.category.org.hibernate.validator.internal.util.Version=WARN -log4j.category.org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration=WARN -log4j.category.org.springframework.boot.actuate.endpoint.jmx=WARN -log4j.category.org.thymeleaf=WARN diff --git a/spring-cloud-function-samples/function-sample-aws/src/test/java/example/MapTests.java b/spring-cloud-function-samples/function-sample-aws/src/test/java/example/MapTests.java deleted file mode 100644 index 289a8c9ff..000000000 --- a/spring-cloud-function-samples/function-sample-aws/src/test/java/example/MapTests.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import org.junit.jupiter.api.Test; - -/** - * @author Dave Syer - * - */ -public class MapTests { - - @Test - public void test() { - - } -} diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/.gitignore b/spring-cloud-function-samples/function-sample-azure-blob-trigger/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-azure-blob-trigger/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index c1dd12f17..000000000 Binary files a/spring-cloud-function-samples/function-sample-azure-blob-trigger/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-azure-blob-trigger/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index b74bf7fcd..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/README.adoc b/spring-cloud-function-samples/function-sample-azure-blob-trigger/README.adoc deleted file mode 100644 index daddbd8f7..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/README.adoc +++ /dev/null @@ -1,109 +0,0 @@ -== Azure Functions with Blob Trigger - -IMPORTANT: For a general information about building and deploying `Azure Functions` with Spring Cloud Function, consult the https://docs.spring.io/spring-cloud-function/docs/current/reference/html/azure.html[Azure Adapter] documentation. - -The Blob storage trigger starts a function when a new or updated blob is detected. The blob contents are provided as https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob-input?tabs=in-process%2Cextensionv5&pivots=programming-language-java[input] to the function. - -The Blob storage binding is part of an https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-register#extension-bundles[extension bundle], specified in your https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob?tabs=in-process%2Cextensionv5%2Cextensionv3&pivots=programming-language-java#install-bundle[host.json] file. - - -=== Usage - -For local Azure Storage development you need https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite?tabs=visual-studio[Azurite emulator]. -For the emulator you can run a docker container (see below) or use the https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite?tabs=visual-studio-code[Visual-Studio-Code extension]. - -Here is how to start the `Azure emulator` as docker container: - -[source,shell] ----- -docker run --name azurite --rm -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite ----- - -==== Package Staging folder - -Use the script below to package your staging folder: - -[source,shell] ----- -./mvnw clean package ----- - -==== Run Azure Functions locally - -Use the script below to run the function locally. - -[source,shell] ----- -./mvnw azure-functions:run ----- - -Use the https://azure.microsoft.com/en-us/products/storage/storage-explorer/[Azure Storage Explorer] to access the Emulator Storage Account. - -Under the `Blob Containers` create 3 new containers: `test-trigger`, `test-input`, `test-output`. -Then upload the `src/test/resource/sample.txt` file into the `test-input` and the `test-trigger` folders in this order. - -The appearance of the `sample.txt` file in the `test-trigger` folder triggers the `blobTest` function handler, that would look up for a file with the same name (because we used the `{name}` convention in the @BlobInput path) from the `test-input` folder. -Later is passed through the auto-wired `uppercase` service and the result is saved in the `test-output` folder. -Verify that the newly created file in `test-output` is in capitalized letters. - - -==== Deploy Azure Functions to Azure Cloud - -Make sure you are logged in your Azure account. - -[source,shell] ----- -az login ----- - -then build and deploy - -[source,shell] ----- -./mvnw clean package -./mvnw azure-functions:deploy ----- - - -==== Debug locally - -Run the function in debug mode. - -[source,shell] ----- -./mvnw azure-functions:run -DenableDebug ----- - -Alternatively and the `JAVA_OPTS` value to your `local.settings.json` like this: - -[source,json] ----- -{ - "IsEncrypted": false, - "Values": { - ... - "FUNCTIONS_WORKER_RUNTIME": "java", - "JAVA_OPTS": "-Djava.net.preferIPv4Stack=true -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=127.0.0.1:5005" - } -} ----- - - -For VSCode remote debug use configuration like this: - -[source,json] ----- -{ - "version": "0.2.0", - "configurations": [ - { - "type": "java", - "name": "Attach to Remote Program", - "request": "attach", - "hostName": "localhost", - "port": "5005" - }, - ... - ] -} ----- \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/mvnw b/spring-cloud-function-samples/function-sample-azure-blob-trigger/mvnw deleted file mode 100755 index 8a8fb2282..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/mvnw +++ /dev/null @@ -1,316 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`\\unset -f command; \\command -v java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/mvnw.cmd b/spring-cloud-function-samples/function-sample-azure-blob-trigger/mvnw.cmd deleted file mode 100644 index 1d8ab018e..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/mvnw.cmd +++ /dev/null @@ -1,188 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/pom.xml b/spring-cloud-function-samples/function-sample-azure-blob-trigger/pom.xml deleted file mode 100644 index 257c94597..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/pom.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - com.example.azure.di - azure-blob-trigger-demo - 0.0.1-SNAPSHOT - azure-blob-trigger-demo - Demo project for Spring Boot - - 17 - 1.0.28.RELEASE - - com.example.azure.di.azureblobtriggerdemo.AzureBlobTriggerDemoApplication - - - 1.22.0 - spring-cloud-function-samples - westus - java-functions-group - java-functions-app-service-plan - EP1 - - - - - org.springframework.cloud - spring-cloud-function-adapter-azure - 4.1.0-SNAPSHOT - - - - org.springframework.boot - spring-boot-starter - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - com.microsoft.azure - azure-functions-maven-plugin - ${azure.functions.maven.plugin.version} - - - ${functionAppName} - ${functionResourceGroup} - ${functionAppRegion} - ${functionAppServicePlanName} - ${functionPricingTier} - - ${project.basedir}/src/main/resources/host.json - ${project.basedir}/src/main/resources/local.settings.json - - - linux - 17 - - - 7072 - - - - FUNCTIONS_EXTENSION_VERSION - ~4 - - - - - - package-functions - - package - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${spring-boot-thin-layout.version} - - - - - - - diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplication.java deleted file mode 100644 index ce0ab0212..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplication.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.azure.di.azureblobtriggerdemo; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class AzureBlobTriggerDemoApplication { - - public static void main(String[] args) { - SpringApplication.run(AzureBlobTriggerDemoApplication.class, args); - } - - @Bean - public Function uppercase() { - return payload -> new String(payload).toUpperCase().getBytes(); - } -} diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/MyBlobFunction.java b/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/MyBlobFunction.java deleted file mode 100644 index 06a26c7f4..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/MyBlobFunction.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.azure.di.azureblobtriggerdemo; - -import java.util.function.Function; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.OutputBinding; -import com.microsoft.azure.functions.annotation.BindingName; -import com.microsoft.azure.functions.annotation.BlobInput; -import com.microsoft.azure.functions.annotation.BlobOutput; -import com.microsoft.azure.functions.annotation.BlobTrigger; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.StorageAccount; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Azure Functions with Azure Storage Blob. - * https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob-trigger?tabs=java - * - * The Blob storage binding is part of an extension bundle, which is specified in your host.json project file. - */ - -@Component -public class MyBlobFunction { - - @Autowired - private Function uppercase; - - /** - * 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. The location of the blob is provided in the path parameter. Example - - * test-trigger/{name} below - */ - @FunctionName("BlobTrigger") - @StorageAccount("AzureWebJobsStorage") - public void blobTest( - @BlobTrigger(name = "triggerBlob", path = "test-trigger/{name}", dataType = "binary") byte[] triggerBlob, - @BindingName("name") String fileName, - @BlobInput(name = "inputBlob", path = "test-input/{name}", dataType = "binary") byte[] inputBlob, - @BlobOutput(name = "outputBlob", path = "test-output/{name}", dataType = "binary") OutputBinding outputBlob, - final ExecutionContext context) { - - context.getLogger().info("Java Blob trigger function blobTest processed a blob.\n Name: " - + fileName + "\n Size: " + triggerBlob.length + " Bytes"); - - outputBlob.setValue(uppercase.apply(inputBlob)); - } -} diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/resources/application.properties deleted file mode 100644 index 8b1378917..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/resources/host.json b/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/resources/host.json deleted file mode 100644 index 10d0c0748..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/resources/host.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "2.0", - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[3.*, 4.0.0)" - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/resources/local.settings.json b/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/resources/local.settings.json deleted file mode 100644 index adce8b884..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/resources/local.settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "AzureWebJobsDashboard": "", - "FUNCTIONS_WORKER_RUNTIME": "java" - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/test/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplicationTests.java b/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/test/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplicationTests.java deleted file mode 100644 index 2b9b92ad5..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/test/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.azure.di.azureblobtriggerdemo; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class AzureBlobTriggerDemoApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/test/resource/sample.txt b/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/test/resource/sample.txt deleted file mode 100644 index 089443f7f..000000000 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/test/resource/sample.txt +++ /dev/null @@ -1,3 +0,0 @@ -Authoritarianism begins when we can no longer tell the difference between the true and the appealing. -At the same time, the cynic who decides that there is no truth at all is the citizen who welcomes the tyrant. -― Timothy Snyder, The Road to Unfreedom \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/.gitignore b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/.gitignore deleted file mode 100644 index c2065bc26..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -HELP.md -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/README.adoc b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/README.adoc deleted file mode 100644 index 395492529..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/README.adoc +++ /dev/null @@ -1,51 +0,0 @@ -== Spring Cloud Function on Azure - Gradle Example - -Show how to build SCF/Azure application with the Azure Function Gradle Plugin. - -IMPORTANT: For a general information about building and deploying `Azure Functions` with Spring Cloud Function, consult the https://docs.spring.io/spring-cloud-function/docs/current/reference/html/azure.html[Azure Adapter] documentation. - -=== Usage - -==== Package Staging folder - -Use the script below to package your staging folder: - -[source,shell] ----- -./gradlew azureFunctionsPackage ----- - -==== Run Azure Functions locally - -Use the script below to run the function locally. - -[source,shell] ----- -./gradlew azureFunctionsRun ----- - -Once up and running test with: - -[source,shell] ----- -curl -X POST http://localhost:7071/api/bean -d 'low case test' ----- - -should trigger an output like: `LOW CASE TEST%` - -TIP: To debug your functions, please add `localDebug = "transport=dt_socket,server=y,suspend=n,address=5005"` to the `azurefunctions` section of your `build.gradle`. - -IMPORTANT: After completing the sample run `./gradlew clean` to clean the hanging `azure-functions-java-worker.jar` processes. - -==== Deploy Azure Functions to Azure Cloud - -[source,shell] ----- -./gradlew azureFunctionsDeploy ----- - -=== References - -- https://github.com/microsoft/azure-gradle-plugins/tree/master/azure-functions-gradle-plugin[Azure Functions Gradle Plugin] -- https://learn.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-gradle[Use Java and Gradle to create and publish a function to Azure] -- https://docs.spring.io/spring-cloud-function/docs/current/reference/html/azure.html[Spring Cloud Function - Microsoft Azure Adapter] \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/build.gradle b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/build.gradle deleted file mode 100644 index 57488a215..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/build.gradle +++ /dev/null @@ -1,62 +0,0 @@ -plugins { - id 'java' - id 'io.spring.dependency-management' version '1.1.0' - id "com.microsoft.azure.azurefunctions" version "1.11.0" -} - -apply plugin: 'java' -apply plugin: "com.microsoft.azure.azurefunctions" - -group = 'org.scf.azure' -version = '0.0.1-SNAPSHOT' - -java { - sourceCompatibility = '17' - targetCompatibility = '17' - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } -} - -jar { - manifest { - attributes( - // The main class is compulsory. Set it to point your SpringBootApplication. - "Main-Class": "org.scf.azure.gradle.GradleDemoApplication" - ) - } -} - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter' - implementation "org.springframework.cloud:spring-cloud-function-adapter-azure:4.1.0-SNAPSHOT" -} - -tasks.named('test') { - useJUnitPlatform() -} - -// Configuration options: https://github.com/microsoft/azure-gradle-plugins/wiki/Configuration -azurefunctions { - resourceGroup = 'java-functions-group' - appName = 'scff-azure-gradle-sample' - region = 'westus' - appServicePlanName = 'java-functions-app-service-plan' - pricingTier = 'EP1' - runtime { - os = 'linux' - javaVersion = '11' - } - auth { - type = 'azure_cli' - } - appSettings { - FUNCTIONS_EXTENSION_VERSION = '~4' - } - localDebug = "transport=dt_socket,server=y,suspend=n,address=5005" -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index c1962a79e..000000000 Binary files a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 37aef8d3f..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip -networkTimeout=10000 -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradlew b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradlew deleted file mode 100755 index aeb74cbb4..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradlew +++ /dev/null @@ -1,245 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradlew.bat b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradlew.bat deleted file mode 100644 index 6689b85be..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/host.json b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/host.json deleted file mode 100644 index 5d8f8a197..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/host.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "2.0", - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[4.*, 5.0.0)" - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/settings.gradle b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/settings.gradle deleted file mode 100644 index 69c977517..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'gradle-demo' diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/java/org/scf/azure/gradle/GradleDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/java/org/scf/azure/gradle/GradleDemoApplication.java deleted file mode 100644 index 3d5902df8..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/java/org/scf/azure/gradle/GradleDemoApplication.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.scf.azure.gradle; - -import java.util.Optional; -import java.util.function.Function; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.adapter.azure.AzureFunctionUtil; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; - -@SpringBootApplication -public class GradleDemoApplication { - - public static void main(String[] args) { - SpringApplication.run(GradleDemoApplication.class, args); - } - - /** - * Plain Spring bean (not Spring Cloud Functions!) - */ - @Autowired - private Function, String> uppercase; - - @FunctionName("bean") - public String plainBean( - @HttpTrigger(name = "req", methods = { HttpMethod.GET, - HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - - // Inject the ExecutionContext as Message header - Message enhancedRequest = (Message) AzureFunctionUtil.enhanceInputIfNecessary( - request.getBody().get(), - context); - - return this.uppercase.apply(enhancedRequest); - } - - @Bean - public Function, String> uppercase() { - return message -> { - ExecutionContext context = (ExecutionContext) message.getHeaders().get(AzureFunctionUtil.EXECUTION_CONTEXT); - - String updatedPayload = message.getPayload().toUpperCase(); - - context.getLogger().info("Azure Test: " + updatedPayload); - - return message.getPayload().toUpperCase(); - }; - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/resources/application.properties deleted file mode 100644 index 1b45c68af..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.main.allow-circular-references=true \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/.gitignore b/spring-cloud-function-samples/function-sample-azure-http-trigger/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-azure-http-trigger/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index c1dd12f17..000000000 Binary files a/spring-cloud-function-samples/function-sample-azure-http-trigger/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-azure-http-trigger/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index b74bf7fcd..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/README.adoc b/spring-cloud-function-samples/function-sample-azure-http-trigger/README.adoc deleted file mode 100644 index d5cbed220..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/README.adoc +++ /dev/null @@ -1,94 +0,0 @@ -== Azure Functions HTTP triggers sample - -IMPORTANT: For a general information about building and deploying `Azure Functions` with Spring Cloud Function, consult the https://docs.spring.io/spring-cloud-function/docs/current/reference/html/azure.html[Azure Adapter] documentation. - -Azure Functions may be invoked via HTTP requests to build serverless APIs. -Find more about the https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook?tabs=in-process%2Cfunctionsv2&pivots=programming-language-java[HTTP triggers]. - -=== Usage - -==== Package Staging folder - -Use the script below to package your staging folder: - -[source,shell] ----- -./mvnw clean package ----- - -==== Run Azure Functions locally - -Use the script below to run the function locally. - -[source,shell] ----- -./mvnw azure-functions:run ----- - -NOTE: To run locally on top of `Azure Functions`, and to deploy to your live Azure environment, you will need `Azure Functions Core Tools` installed along with the Azure CLI (see https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser#configure-your-local-environment[here]). - -NOTE: https://github.com/Azure/azure-functions-core-tools[Azure Functions Core Tools] version `4.0.5030` or newer is required! - -For some configuration you would need the https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator[Azurite emulator] as well. - - -==== Deploy Azure Functions to Azure Cloud - -Make sure you are logged in your Azure account. - -[source,shell] ----- -az login ----- - -then build and deploy - -[source,shell] ----- -./mvnw clean package -./mvnw azure-functions:deploy ----- - - -==== Debug locally - -Run the function in debug mode. - -[source,shell] ----- -./mvnw azure-functions:run -DenableDebug ----- - -Alternatively and the `JAVA_OPTS` value to your `local.settings.json` like this: - -[source,json] ----- -{ - "IsEncrypted": false, - "Values": { - ... - "FUNCTIONS_WORKER_RUNTIME": "java", - "JAVA_OPTS": "-Djava.net.preferIPv4Stack=true -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=127.0.0.1:5005" - } -} ----- - - -For VSCode remote debug use configuration like this: - -[source,json] ----- -{ - "version": "0.2.0", - "configurations": [ - { - "type": "java", - "name": "Attach to Remote Program", - "request": "attach", - "hostName": "localhost", - "port": "5005" - }, - ... - ] -} ----- \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/mvnw b/spring-cloud-function-samples/function-sample-azure-http-trigger/mvnw deleted file mode 100755 index 8a8fb2282..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/mvnw +++ /dev/null @@ -1,316 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`\\unset -f command; \\command -v java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/mvnw.cmd b/spring-cloud-function-samples/function-sample-azure-http-trigger/mvnw.cmd deleted file mode 100644 index 1d8ab018e..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/mvnw.cmd +++ /dev/null @@ -1,188 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/pom.xml b/spring-cloud-function-samples/function-sample-azure-http-trigger/pom.xml deleted file mode 100644 index 50fd06a98..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/pom.xml +++ /dev/null @@ -1,111 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - com.example.azure.di - azure-httptrigger-demo - 0.0.1-SNAPSHOT - azure-httptrigger-demo - - Demo Spring Boot, Azure Function - HttpTrigger (DI adapter) - - - 17 - 1.0.28.RELEASE - - - com.example.azure.di.httptriggerdemo.HttpTriggerDemoApplication - - - 1.22.0 - spring-cloud-function-samples - westus - java-functions-group - java-functions-app-service-plan - EP1 - - - - - org.springframework.cloud - spring-cloud-function-adapter-azure - 4.1.0-SNAPSHOT - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - com.microsoft.azure - azure-functions-maven-plugin - ${azure.functions.maven.plugin.version} - - - ${functionAppName} - ${functionResourceGroup} - ${functionAppRegion} - ${functionAppServicePlanName} - ${functionPricingTier} - - ${project.basedir}/src/main/resources/host.json - - - linux - 17 - - - 7072 - - - - FUNCTIONS_EXTENSION_VERSION - ~4 - - - - - - package-functions - - package - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${spring-boot-thin-layout.version} - - - - - - - diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/HttpTriggerDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/HttpTriggerDemoApplication.java deleted file mode 100644 index fc7bd8ea3..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/HttpTriggerDemoApplication.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.azure.di.httptriggerdemo; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class HttpTriggerDemoApplication { - - @Bean - public Function echo() { - return payload -> payload; - } - - @Bean - public Function uppercase() { - return payload -> payload.toUpperCase(); - } - - @Bean - public Function reverse() { - return payload -> new StringBuilder(payload).reverse().toString(); - } - - public static void main(String[] args) { - SpringApplication.run(HttpTriggerDemoApplication.class, args); - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/MyAzureFunction.java b/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/MyAzureFunction.java deleted file mode 100644 index 6838aa7d7..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/MyAzureFunction.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.azure.di.httptriggerdemo; - -import java.util.Optional; -import java.util.function.Function; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.stereotype.Component; - -@Component -public class MyAzureFunction { - - /** - * Plain Spring bean (not Spring Cloud Functions!) - */ - @Autowired - private Function echo; - - /** - * Plain Spring bean (not Spring Cloud Functions!) - */ - @Autowired - private Function uppercase; - - /** - * The FunctionCatalog leverages the Spring Cloud Function framework. - */ - @Autowired - private FunctionCatalog functionCatalog; - - @FunctionName("bean") - public String plainBeans( - @HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - - return echo.andThen(uppercase).apply(request.getBody().get()); - } - - @FunctionName("scf") - public String springCloudFunction( - @HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - - // Use SCF composition. Composed functions are not just spring beans but SCF such. - Function composed = this.functionCatalog.lookup("echo|reverse|uppercase"); - - return (String) composed.apply(request.getBody().get()); - } -} diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/resources/application.properties deleted file mode 100644 index 8b1378917..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/resources/host.json b/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/resources/host.json deleted file mode 100644 index 10d0c0748..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/resources/host.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "2.0", - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[3.*, 4.0.0)" - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/test/java/com/example/azure/di/httptriggerdemo/HttptriggerDemoApplicationTests.java b/spring-cloud-function-samples/function-sample-azure-http-trigger/src/test/java/com/example/azure/di/httptriggerdemo/HttptriggerDemoApplicationTests.java deleted file mode 100644 index dbc76d294..000000000 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/test/java/com/example/azure/di/httptriggerdemo/HttptriggerDemoApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.azure.di.httptriggerdemo; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class HttptriggerDemoApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/.gitignore b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/.gitignore deleted file mode 100644 index 7ed0d6b67..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/README.md b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/README.md deleted file mode 100644 index 5b5fc311c..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/README.md +++ /dev/null @@ -1,109 +0,0 @@ -# Azure Function with Kafka Trigger & Output Binding - -Spring Cloud Function example for implementing an Azure functions with [KafkaTrigger](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-kafka-trigger?tabs=in-process%2Cconfluent&pivots=programming-language-java) and [Kafka Binding](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-kafka-output?tabs=in-process%2Cconfluent&pivots=programming-language-java) support. - -The Azure function is triggered by messages sent on the `trigger` topic and in turn calls the `uppercase` SCF with the trigger payload. The SCF capitalizes the input json value fields and sends the result to an output Kafka topic called: `output`. - -## Running Locally - -First start a Kafka server locally. -The `./src/main/resources/docker-compose-demo.yaml` helps to start locally Zookeeper, Kafka and Kafka UI. - -``` -docker-compose -f ./src/main/resources/docker-compose-demo.yaml up -``` -You can reach the Kafka UI (Redpanda) dashboard on http://localhost:8080/topics - -![](./src/main/doc/kafka-ui-topics.png) - -The docker-compose pre-creates the `trigger` and `output` topics used by the function. - -Next build and run the Azure function: - -``` -./mvnw clean package -./mvnw azure-functions:run -``` - -From the Kafka UI, got to the `trigger` topic view (http://localhost:8080/topics/trigger), select `Actions/Publish Message` and submit a new JSON message: -```json -{ "foo" : "bar"} -``` - -![](./src/main/doc/kafka-publish-message.png) - - -Push the `Publish` button and let the function do its job and check the `output` topic (http://localhost:8080/topics/output) : - -![](./src/main/doc/output-topic.png) - -e.g.the `bar` is in uppercase: - -```json -{ "foo" : "BAR"} -``` - - - -## Running on Azure (TODO: WIP) - -Make sure you are logged in your Azure account. -``` -az login -``` - -Build and deploy - -``` -./mvnw clean package -./mvnw azure-functions:deploy -``` - -## Implementation - -Configure the [Kafka extension](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-kafka?tabs=in-process%2Cportal&pivots=programming-language-java#hostjson-settings) in the `host.json` settings: - -```json -{ - "functionTimeout": "00:05:00", - "version": "2.0", - "extensions": { - "kafka": { - "maxBatchSize": 64, - "SubscriberIntervalInSeconds": 1, - "ExecutorChannelCapacity": 1, - "ChannelFullRetryIntervalInMs": 50 - } - }, - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[3.3.0, 4.0.0)" - } -} -``` - -Also to allow your functions to scale properly on the Premium plan when using Kafka triggers and bindings, you need to [enable runtime scale monitoring](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-kafka?tabs=in-process%2Cportal&pivots=programming-language-java#enable-runtime-scaling). - - -## Notes - -* Disable the `spring-boot-maven-plugin` in favor of the `azure-functions-maven-plugin`. -* Exclude the `org.springframework.boot:spring-boot-starter-logging` dependency from the `org.springframework.cloud:spring-cloud-function-adapter-azure`. -* In `local.settings.json` set the local values for the `%BrokerList`, `%ConfluentCloudUsername%` and the `%TriggerKafkaTopic%` trigger and binding variables: - -``` -{ - "IsEncrypted": false, - "Values": { -... - "BrokerList": "localhost:9092", - "ConfluentCloudUsername": "test", - "TriggerKafkaTopic": "trigger" - } -} -``` - -## References - -* [Apache Kafka for Confluent Cloud - Azure portal](https://learn.microsoft.com/en-us/azure/partner-solutions/apache-kafka-confluent-cloud/create) - show how to create an instance of Apache Kafka for Confluent Cloud. -- [Create a Cluster in Confluent Cloud](https://docs.confluent.io/cloud/current/clusters/create-cluster.html#create-a-cluster-in-ccloud) \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/mvnw b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/mvnw deleted file mode 100755 index 8a8fb2282..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/mvnw +++ /dev/null @@ -1,316 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`\\unset -f command; \\command -v java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/mvnw.cmd b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/mvnw.cmd deleted file mode 100644 index 1d8ab018e..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/mvnw.cmd +++ /dev/null @@ -1,188 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/pom.xml b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/pom.xml deleted file mode 100644 index 304bc8c40..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/pom.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - example.scf.azure - kafka-trigger-azure-spring-function - 0.0.1-SNAPSHOT - kafka-trigger-demo - Demo project for Spring Boot - - 17 - 4.1.0-SNAPSHOT - 1.0.28.RELEASE - - example.KafkaTriggerDemoApplication - - example-spring-function-resource-group - kafka-trigger-azure-spring-function - westeurope - ${project.build.directory}/azure-functions/${functionAppName} - java-functions-app-service-plan - 1.22.0 - - - - - org.springframework.cloud - spring-cloud-function-context - ${spring-cloud-function-dependencies.version} - - - org.springframework.cloud - spring-cloud-function-adapter-azure - ${spring-cloud-function-dependencies.version} - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - com.microsoft.azure - azure-functions-maven-plugin - ${azure.functions.maven.plugin.version} - - ${functionResourceGroup} - ${functionAppName} - ${functionAppRegion} - ${functionAppServicePlanName} - - ${project.basedir}/src/main/azure/host.json - ${project.basedir}/src/main/azure/local.settings.json - - - linux - 17 - - - - - FUNCTIONS_EXTENSION_VERSION - ~4 - - - FUNCTIONS_WORKER_RUNTIME - java - - - - - - package-functions - - package - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${spring-boot-thin-layout.version} - - - - - - diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/azure/host.json b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/azure/host.json deleted file mode 100644 index 8bb952206..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/azure/host.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "functionTimeout": "00:05:00", - "version": "2.0", - "extensions": { - "kafka": { - "maxBatchSize": 64, - "SubscriberIntervalInSeconds": 1, - "ExecutorChannelCapacity": 1, - "ChannelFullRetryIntervalInMs": 50 - } - }, - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[3.3.0, 4.0.0)" - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/azure/local.settings.json b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/azure/local.settings.json deleted file mode 100644 index 87b48b54b..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/azure/local.settings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "AzureWebJobsDashboard": "", - "FUNCTIONS_WORKER_RUNTIME": "java", - - "BrokerList": "localhost:9092", - "ConfluentCloudUsername": "test", - "ConfluentCloudPassword": "test", - "TriggerKafkaTopic": "trigger" - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/doc/kafka-publish-message.png b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/doc/kafka-publish-message.png deleted file mode 100644 index f8122d360..000000000 Binary files a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/doc/kafka-publish-message.png and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/doc/kafka-ui-topics.png b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/doc/kafka-ui-topics.png deleted file mode 100644 index 3d2fd4ac6..000000000 Binary files a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/doc/kafka-ui-topics.png and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/doc/output-topic.png b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/doc/output-topic.png deleted file mode 100644 index 8aa11a41f..000000000 Binary files a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/doc/output-topic.png and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/KafkaTriggerDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/KafkaTriggerDemoApplication.java deleted file mode 100644 index bb01e7c8a..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/KafkaTriggerDemoApplication.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example; - -import java.util.Map; -import java.util.function.Function; - -import example.entity.KafkaEntity; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; - -@SpringBootApplication -public class KafkaTriggerDemoApplication { - - public static void main(String[] args) { - SpringApplication.run(KafkaTriggerDemoApplication.class, args); - } - - @Bean - public Function, String> uppercase(JsonMapper mapper) { - return message -> { - - // Convert the message payload into Azure's KafkaEntity format. - KafkaEntity kafkaEntity = mapper.fromJson(message.getPayload(), KafkaEntity.class); - - // Business logic: convert the JSON string values into uppercase. - if (kafkaEntity.getValue() != null) { - Map valueMap = mapper.fromJson(kafkaEntity.getValue(), Map.class); - if (valueMap != null) { - valueMap.forEach((k, v) -> valueMap.put(k, - v != null && v instanceof String ? ((String) v).toUpperCase() : null)); - return mapper.toString(valueMap); - } - } - - return mapper.toString(null); - }; - } -} diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/UppercaseHandler.java b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/UppercaseHandler.java deleted file mode 100644 index 2e7f33b11..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/UppercaseHandler.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import java.util.function.Function; - -import com.microsoft.azure.functions.BrokerAuthenticationMode; -import com.microsoft.azure.functions.BrokerProtocol; -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.OutputBinding; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.KafkaOutput; -import com.microsoft.azure.functions.annotation.KafkaTrigger; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.stereotype.Component; - -@Component -public class UppercaseHandler { - - @Autowired - private Function, String> uppercase; - - @FunctionName("KafkaTrigger") - public void execute( - @KafkaTrigger( - name = "KafkaTrigger", - topic = "%TriggerKafkaTopic%", - brokerList = "%BrokerList%", - consumerGroup = "$Default", - username = "%ConfluentCloudUsername%", - password = "%ConfluentCloudPassword%", - authenticationMode = BrokerAuthenticationMode.PLAIN, - protocol = BrokerProtocol.PLAINTEXT, - // protocol = BrokerProtocol.SASLSSL, - // sslCaLocation = "confluent_cloud_cacert.pem", // Enable this line for windows. - dataType = "string") String kafkaEventData, - @KafkaOutput( - name = "kafkaOutput", - topic = "output", - brokerList="%BrokerList%", - username = "%ConfluentCloudUsername%", - password = "%ConfluentCloudPassword%", - authenticationMode = BrokerAuthenticationMode.PLAIN, - // sslCaLocation = "confluent_cloud_cacert.pem", // Enable this line for windows. - protocol = BrokerProtocol.PLAINTEXT - // protocol = BrokerProtocol.SASLSSL - ) OutputBinding output, - final ExecutionContext context) { - - context.getLogger().info(kafkaEventData); - - Message message = MessageBuilder.withPayload(kafkaEventData).build(); - - String response = uppercase.apply(message); - - output.setValue(response); - } -} diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/entity/KafkaEntity.java b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/entity/KafkaEntity.java deleted file mode 100644 index af9be822b..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/entity/KafkaEntity.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.entity; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class KafkaEntity { - @JsonProperty("Offset") - private int offset; - @JsonProperty("Partition") - private int partition; - @JsonProperty("Timestamp") - private String timestamp; - @JsonProperty("Topic") - private String topic; - @JsonProperty("Key") - private String key; - @JsonProperty("Value") - private String value; - @JsonProperty("Headers") - private KafkaHeaders[] headers; - - public int getOffset() { - return offset; - } - - public void setOffset(int offset) { - this.offset = offset; - } - - public int getPartition() { - return partition; - } - - public void setPartition(int partition) { - this.partition = partition; - } - - public String getTimestamp() { - return timestamp; - } - - public void setTimestamp(String timestamp) { - this.timestamp = timestamp; - } - - public String getTopic() { - return topic; - } - - public void setTopic(String topic) { - this.topic = topic; - } - - public String getValue() { - return value; - } - - - public void setValue(String value) { - this.value = value; - } - - public KafkaHeaders[] getHeaders() { - return headers; - } - - public void setHeaders(KafkaHeaders[] headers) { - this.headers = headers; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/entity/KafkaHeaders.java b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/entity/KafkaHeaders.java deleted file mode 100644 index af26e25f7..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/entity/KafkaHeaders.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package example.entity; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class KafkaHeaders { - @JsonProperty("Key") - private String key; - @JsonProperty("Value") - private String value; - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/resources/docker-compose-demo.yaml b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/resources/docker-compose-demo.yaml deleted file mode 100644 index c184f5a1d..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/resources/docker-compose-demo.yaml +++ /dev/null @@ -1,63 +0,0 @@ -version: '2.1' - -services: - zookeeper: - image: zookeeper:3.4.9 - hostname: zookeeper - ports: - - "2181:2181" - environment: - ZOO_MY_ID: 1 - ZOO_PORT: 2181 - ZOO_SERVERS: server.1=zookeeper:2888:3888 - # volumes: - # - ./zk-single-kafka-single/zookeeper/data:/data - # - ./zk-single-kafka-single/zookeeper/datalog:/datalog - - kafka1: - image: confluentinc/cp-kafka:7.2.2 - hostname: kafka1 - ports: - - "9092:9092" - environment: - KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL://kafka1:19092,LISTENER_DOCKER_EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092 - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LISTENER_DOCKER_INTERNAL:PLAINTEXT,LISTENER_DOCKER_EXTERNAL:PLAINTEXT - KAFKA_INTER_BROKER_LISTENER_NAME: LISTENER_DOCKER_INTERNAL - KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181" - KAFKA_BROKER_ID: 1 - KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO" - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 - # volumes: - # - ./zk-single-kafka-single/kafka1/data:/var/lib/kafka/data - depends_on: - - zookeeper - - - init-kafka: - image: confluentinc/cp-kafka:7.2.2 - depends_on: - - kafka1 - entrypoint: [ '/bin/sh', '-c' ] - command: | - " - # blocks until kafka is reachable - kafka-topics --bootstrap-server kafka1:19092 --list - - echo -e 'Creating kafka topics' - kafka-topics --bootstrap-server kafka1:19092 --create --if-not-exists --topic trigger --replication-factor 1 --partitions 1 - kafka-topics --bootstrap-server kafka1:19092 --create --if-not-exists --topic output --replication-factor 1 --partitions 1 - - echo -e 'Successfully created the following topics:' - kafka-topics --bootstrap-server kafka1:19092 --list - " - - kconsole: - image: docker.redpanda.com/vectorized/console:latest - restart: on-failure - hostname: kconsole - ports: - - "8080:8080" - environment: - KAFKA_BROKERS: "kafka1:19092" - depends_on: - - kafka1 \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/wrapper/maven-wrapper.jar deleted file mode 100644 index c1dd12f17..000000000 Binary files a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/wrapper/maven-wrapper.properties deleted file mode 100644 index b74bf7fcd..000000000 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/.gitignore b/spring-cloud-function-samples/function-sample-azure-time-trigger/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-azure-time-trigger/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index c1dd12f17..000000000 Binary files a/spring-cloud-function-samples/function-sample-azure-time-trigger/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-azure-time-trigger/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index b74bf7fcd..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/README.adoc b/spring-cloud-function-samples/function-sample-azure-time-trigger/README.adoc deleted file mode 100644 index fc5fc9d23..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/README.adoc +++ /dev/null @@ -1,122 +0,0 @@ -== Azure TimerTrigger Function - -IMPORTANT: For a general information about building and deploying `Azure Functions` with Spring Cloud Function, consult the https://docs.spring.io/spring-cloud-function/docs/current/reference/html/azure.html[Azure Adapter] documentation. - -Spring Cloud Function example for implementing https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer?tabs=in-process&pivots=programming-language-java[Timer trigger for Azure Functions]. - -=== Running Locally - -NOTE: To run locally on top of `Azure Functions`, and to deploy to your live Azure environment, you will need `Azure Functions Core Tools` installed along with the Azure CLI (see https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser#configure-your-local-environment[here]) as well as the Use https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator[Azurite emulator] for local Azure Storage development. For the emulator you can run a docker container (see below) or use the https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite?tabs=visual-studio-code[Visual-Studio-Code extension]. - -Here is how ot start the `Azure emulator` as docker container: - -[source,shell] ----- - -docker run --name azurite --rm -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite ----- - -Then build and run the sample: - -[source,shell] ----- -./mvnw clean package -./mvnw azure-functions:run ----- - -The timer triggers the function every minute. -In result the the `uppercase` Spring Cloud Function is called and uppercase the timeInfo and logs it into the context. - -``` -[2022-10-11T08:53:00.011Z] Execution Context Log - TimeInfo: {"Schedule":{"AdjustForDST":true},"ScheduleStatus":{"Last":"2022-10-11T10:52:00.003967+02:00","Next":"2022-10-11T10:53:00+02:00","LastUpdated":"2022-10-11T10:52:00.003967+02:00"},"IsPastDue":false} -``` - -The `executeExpRetry` handler demonstrates how to handle errors using the https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-error-pages?tabs=exponential-backoff%2Cin-process&pivots=programming-language-java#retry-policies[Retry policies]. -Sample emulates 3 errors on the first 3 executions and then continues as expected. - -=== Running on Azure - -Make sure you are logged in your Azure account. - -[source,shell] ----- -az login ----- - -Build and deploy - -[source,shell] ----- -./mvnw clean package -./mvnw azure-functions:deploy ----- - -=== Implementation details - -The `spring-cloud-function-adapter-azure` dependency activates the AzureFunctionInstanceInjector: - -[source,xml] ----- - - org.springframework.cloud - spring-cloud-function-adapter-azure - ----- - -(Version 4.x.x or higher) - - -The `uppercase` function with signature `Function, Void> uppercase()` is defined as `@Bean` in the TimeTriggerDemoApplication context. - - -[source,java] ----- - @Bean - public Consumer> uppercase() { - return message -> { - String timeInfo = message.getPayload(); - String value = timeInfo.toUpperCase(); - - logger.info("Timer is triggered with TimeInfo: " + value); - - // (Optionally) access and use the Azure function context. - ExecutionContext context = (ExecutionContext) message.getHeaders().get(UppercaseHandler.EXECUTION_CONTEXT); - context.getLogger().info("Execution Context Log - TimeInfo: " + value); - - // No response. - }; - } ----- - -TIP: The uppercase function does not return value (e.g. Void output type) and is backed by `java.util.Consumer`. - -The `UppercaseHandler` (marked as Spring `@Component`) implements the Azure function using the Azure Function Java API. Furthermore as Spring component the UppercaseHandler leverages the Spring configuration and programming model to inject the necessary services required by the functions. - -[source,java] ----- -@Component -public class UppercaseHandler { - - public static String EXECUTION_CONTEXT = "executionContext"; - - @Autowired - private Consumer> uppercase; - - @FunctionName("uppercase") - public void execute(@TimerTrigger(name = "keepAliveTrigger", schedule = "0 */1 * * * *") String timerInfo, - ExecutionContext context) { - - Message message = MessageBuilder - .withPayload(timerInfo) - .setHeader(EXECUTION_CONTEXT, context) - .build(); - - this.uppercase.accept(message); - } -} ----- - -=== Notes - -- Change the `spring-boot-maven-plugin` to `tiny` in favor of the `azure-functions-maven-plugin` jar packaging. -- Add `"AzureWebJobsStorage": "UseDevelopmentStorage=true"` to the `local.settings.json`. diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/mvnw b/spring-cloud-function-samples/function-sample-azure-time-trigger/mvnw deleted file mode 100755 index 8a8fb2282..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/mvnw +++ /dev/null @@ -1,316 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`\\unset -f command; \\command -v java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/mvnw.cmd b/spring-cloud-function-samples/function-sample-azure-time-trigger/mvnw.cmd deleted file mode 100644 index 1d8ab018e..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/mvnw.cmd +++ /dev/null @@ -1,188 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/pom.xml b/spring-cloud-function-samples/function-sample-azure-time-trigger/pom.xml deleted file mode 100644 index dd6487a6e..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/pom.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - com.example.azure.di - azure-timetrigger-demo - 0.0.1-SNAPSHOT - azure-timetrigger-demo - Demo project for Spring Boot - - 17 - 1.0.28.RELEASE - - - com.example.azure.di.timetriggerdemo.TimeTriggerDemoApplication - - - 1.22.0 - spring-cloud-function-samples - westus - java-functions-group - java-functions-app-service-plan - EP1 - - - - - org.springframework.cloud - spring-cloud-function-adapter-azure - 4.1.0-SNAPSHOT - - - - org.springframework.boot - spring-boot-starter - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - com.microsoft.azure - azure-functions-maven-plugin - ${azure.functions.maven.plugin.version} - - - ${functionAppName} - ${functionResourceGroup} - ${functionAppRegion} - ${functionAppServicePlanName} - ${functionPricingTier} - - ${project.basedir}/src/main/resources/host.json - ${project.basedir}/src/main/resources/local.settings.json - - - linux - 17 - - - 7072 - - - - FUNCTIONS_EXTENSION_VERSION - ~4 - - - - - - package-functions - - package - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${spring-boot-thin-layout.version} - - - - - - - diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/TimeTriggerDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/TimeTriggerDemoApplication.java deleted file mode 100644 index e3e10ddc8..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/TimeTriggerDemoApplication.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.azure.di.timetriggerdemo; - -import java.util.function.Consumer; - -import com.microsoft.azure.functions.ExecutionContext; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; - -@SpringBootApplication -public class TimeTriggerDemoApplication { - - private static Log logger = LogFactory.getLog(TimeTriggerDemoApplication.class); - - public static void main(String[] args) { - SpringApplication.run(TimeTriggerDemoApplication.class, args); - } - - @Bean - public Consumer> uppercase() { - return message -> { - String timeInfo = message.getPayload(); - String value = timeInfo.toUpperCase(); - - logger.info("Timer is triggered with TimeInfo: " + value); - - // (Optionally) access and use the Azure function context. - ExecutionContext context = (ExecutionContext) message.getHeaders().get(UppercaseHandler.EXECUTION_CONTEXT); - context.getLogger().info("Execution Context Log - TimeInfo: " + value); - - // No response. - }; - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/UppercaseHandler.java b/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/UppercaseHandler.java deleted file mode 100644 index ca3890206..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/UppercaseHandler.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.azure.di.timetriggerdemo; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.annotation.ExponentialBackoffRetry; -import com.microsoft.azure.functions.annotation.FixedDelayRetry; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.TimerTrigger; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.stereotype.Component; - -@Component -public class UppercaseHandler { - - public static String EXECUTION_CONTEXT = "executionContext"; - - private static AtomicInteger count = new AtomicInteger(); - - @Autowired - private Consumer> uppercase; - - @FunctionName("uppercase") - @FixedDelayRetry(maxRetryCount = 4, delayInterval = "00:00:10") - public void execute(@TimerTrigger(name = "keepAliveTrigger", schedule = "0 */1 * * * *") String timerInfo, - ExecutionContext context) { - - Message message = MessageBuilder - .withPayload(timerInfo) - .setHeader(EXECUTION_CONTEXT, context) - .build(); - - this.uppercase.accept(message); - } - - @FunctionName("uppercaseExpRetry") - @ExponentialBackoffRetry(maxRetryCount = 4, maximumInterval = "00:15:00", minimumInterval = "00:00:03") - public void executeExpRetry(@TimerTrigger(name = "keepAliveTrigger", schedule = "*/10 * * * * *") String timerInfo, - ExecutionContext context) { - - if (count.incrementAndGet() < 3) { - context.getLogger().info("EMULATE ERROR# " + count.get()); - throw new IllegalStateException("Emulated ERROR# " + count.get()); - } - - context.getLogger().info("ERRORLESS EXECUTION"); - - Message message = MessageBuilder - .withPayload(timerInfo) - .setHeader(EXECUTION_CONTEXT, context) - .build(); - - this.uppercase.accept(message); - } -} diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/resources/application.properties deleted file mode 100644 index 8b1378917..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/resources/host.json b/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/resources/host.json deleted file mode 100644 index 10d0c0748..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/resources/host.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "2.0", - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[3.*, 4.0.0)" - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/resources/local.settings.json b/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/resources/local.settings.json deleted file mode 100644 index adce8b884..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/resources/local.settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "AzureWebJobsDashboard": "", - "FUNCTIONS_WORKER_RUNTIME": "java" - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/test/java/com/example/azure/di/timetriggerdemo/TimetriggerDemoApplicationTests.java b/spring-cloud-function-samples/function-sample-azure-time-trigger/src/test/java/com/example/azure/di/timetriggerdemo/TimetriggerDemoApplicationTests.java deleted file mode 100644 index d78399a12..000000000 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/test/java/com/example/azure/di/timetriggerdemo/TimetriggerDemoApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.azure.di.timetriggerdemo; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class TimetriggerDemoApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure-web/.gitignore b/spring-cloud-function-samples/function-sample-azure-web/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-azure-web/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-azure-web/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index cb28b0e37..000000000 Binary files a/spring-cloud-function-samples/function-sample-azure-web/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-azure-web/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-azure-web/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 462686e25..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/spring-cloud-function-samples/function-sample-azure-web/README.adoc b/spring-cloud-function-samples/function-sample-azure-web/README.adoc deleted file mode 100644 index a1da540de..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/README.adoc +++ /dev/null @@ -1,66 +0,0 @@ -== Spring Azure Web Adapter Demo - -A https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web[spring-cloud-function-adapter-azure-web] adapter sample. - -This sample implements a standard Spring Boot Web application, with a REST API for managing a list of country entities. Later are persisted with JPA and H2 database. - -The `spring-cloud-function-adapter-azure-web` adapter provides a light-weight Azure Function forwarding proxy which allows deploying the existing Spring Boot Web application as a Azure Function. - -=== Usage - -==== Build - -[source,shell] ----- -./mvnw clean install ----- - -==== Run Locally - -[source,shell] ----- -./mvnw azure-functions:run ----- - -Then use `curl` to interact with the rest application: - ----- -curl -X GET http://localhost:7072/api/AzureWebAdapter/ ----- - -will output result like `Country Count: 0`. - -Then add few Countries: ----- -curl -X POST -H 'Content-Type:application/json' http://localhost:7072/api/AzureWebAdapter/countries -d '{"name" : "Bulgaria"}' -curl -X POST -H 'Content-Type:application/json' http://localhost:7072/api/AzureWebAdapter/countries -d '{"name" : "Netherlands"}' -curl -X POST -H 'Content-Type:application/json' http://localhost:7072/api/AzureWebAdapter/countries -d '{"name" : "Ukraine"}' ----- - -And check the count again: - ----- -curl -X GET http://localhost:7072/api/AzureWebAdapter/ ----- - -now the output is `Country Count: 3` and `curl -X GET http://localhost:7072/api/AzureWebAdapter/countries` will output: `Countries: Country{id=1, name='Bulgaria'}Country{id=2, name='Netherlands'}Country{id=3, name='Ukraine'}`. - -==== Running on Azure - -Make sure you are logged in your Azure account. - ----- -az login ----- - -and deploy - ----- -./mvnw azure-functions:deploy ----- - - - - - - diff --git a/spring-cloud-function-samples/function-sample-azure-web/mvnw b/spring-cloud-function-samples/function-sample-azure-web/mvnw deleted file mode 100755 index 66df28542..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/mvnw +++ /dev/null @@ -1,308 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.2.0 -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "$(uname)" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME - else - JAVA_HOME="/Library/Java/Home"; export JAVA_HOME - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=$(java-config --jre-home) - fi -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=$(cygpath --unix "$JAVA_HOME") - [ -n "$CLASSPATH" ] && - CLASSPATH=$(cygpath --path --unix "$CLASSPATH") -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && - JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="$(which javac)" - if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=$(which readlink) - if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then - if $darwin ; then - javaHome="$(dirname "\"$javaExecutable\"")" - javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" - else - javaExecutable="$(readlink -f "\"$javaExecutable\"")" - fi - javaHome="$(dirname "\"$javaExecutable\"")" - javaHome=$(expr "$javaHome" : '\(.*\)/bin') - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=$(cd "$wdir/.." || exit 1; pwd) - fi - # end of workaround - done - printf '%s' "$(cd "$basedir" || exit 1; pwd)" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - # Remove \r in case we run on Windows within Git Bash - # and check out the repository with auto CRLF management - # enabled. Otherwise, we may read lines that are delimited with - # \r\n and produce $'-Xarg\r' rather than -Xarg due to word - # splitting rules. - tr -s '\r\n' ' ' < "$1" - fi -} - -log() { - if [ "$MVNW_VERBOSE" = true ]; then - printf '%s\n' "$1" - fi -} - -BASE_DIR=$(find_maven_basedir "$(dirname "$0")") -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR -log "$MAVEN_PROJECTBASEDIR" - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" -if [ -r "$wrapperJarPath" ]; then - log "Found $wrapperJarPath" -else - log "Couldn't find $wrapperJarPath, downloading it ..." - - if [ -n "$MVNW_REPOURL" ]; then - wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - else - wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - fi - while IFS="=" read -r key value; do - # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) - safeValue=$(echo "$value" | tr -d '\r') - case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; - esac - done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" - log "Downloading from: $wrapperUrl" - - if $cygwin; then - wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") - fi - - if command -v wget > /dev/null; then - log "Found wget ... using wget" - [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - log "Found curl ... using curl" - [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" - else - curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" - fi - else - log "Falling back to using Java to download" - javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" - javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaSource=$(cygpath --path --windows "$javaSource") - javaClass=$(cygpath --path --windows "$javaClass") - fi - if [ -e "$javaSource" ]; then - if [ ! -e "$javaClass" ]; then - log " - Compiling MavenWrapperDownloader.java ..." - ("$JAVA_HOME/bin/javac" "$javaSource") - fi - if [ -e "$javaClass" ]; then - log " - Running MavenWrapperDownloader.java ..." - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -# If specified, validate the SHA-256 sum of the Maven wrapper jar file -wrapperSha256Sum="" -while IFS="=" read -r key value; do - case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; - esac -done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" -if [ -n "$wrapperSha256Sum" ]; then - wrapperSha256Result=false - if command -v sha256sum > /dev/null; then - if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then - wrapperSha256Result=true - fi - elif command -v shasum > /dev/null; then - if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then - wrapperSha256Result=true - fi - else - echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." - echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." - exit 1 - fi - if [ $wrapperSha256Result = false ]; then - echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 - echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 - echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 - exit 1 - fi -fi - -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") - [ -n "$CLASSPATH" ] && - CLASSPATH=$(cygpath --path --windows "$CLASSPATH") - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -# shellcheck disable=SC2086 # safe args -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-azure-web/mvnw.cmd b/spring-cloud-function-samples/function-sample-azure-web/mvnw.cmd deleted file mode 100644 index 95ba6f54a..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/mvnw.cmd +++ /dev/null @@ -1,205 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.2.0 -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %WRAPPER_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file -SET WRAPPER_SHA_256_SUM="" -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B -) -IF NOT %WRAPPER_SHA_256_SUM%=="" ( - powershell -Command "&{"^ - "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ - "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ - " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ - " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ - " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ - " exit 1;"^ - "}"^ - "}" - if ERRORLEVEL 1 goto error -) - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-azure-web/pom.xml b/spring-cloud-function-samples/function-sample-azure-web/pom.xml deleted file mode 100644 index 7b733f747..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/pom.xml +++ /dev/null @@ -1,156 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - com.example - azure-web-demo - 0.0.1-SNAPSHOT - azure-web-demo - Spring Cloud Function - Azure Function Web Adapter Demo - - - - 17 - - 1.0.28.RELEASE - 4.1.0-SNAPSHOT - - - com.example.azure.web.AzureWebDemoApplication - - - 1.22.0 - scf-azure-web-sample - westus - java-functions-group - java-functions-app-service-plan - EP1 - - - - - - - - org.springframework.cloud - spring-cloud-function-adapter-azure-web - ${spring-cloud-function-adapter-azure-web.version} - - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - - org.springframework.boot - spring-boot-starter-validation - - - - com.h2database - h2 - runtime - - - - org.springframework.boot - spring-boot-starter-actuator - - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - com.microsoft.azure - azure-functions-maven-plugin - ${azure.functions.maven.plugin.version} - - - ${functionAppName} - ${functionResourceGroup} - ${functionAppRegion} - ${functionAppServicePlanName} - ${functionPricingTier} - - ${project.basedir}/src/main/resources/host.json - - - linux - 17 - - - 7072 - - - - FUNCTIONS_EXTENSION_VERSION - ~4 - - - - - - package-functions - - package - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${spring-boot-thin-layout.version} - - - - - - - - - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - - - spring-milestone - https://repo.spring.io/milestone - - - - diff --git a/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/AzureWebDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/AzureWebDemoApplication.java deleted file mode 100644 index c2e09a03b..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/AzureWebDemoApplication.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.example.azure.web; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; - -@SpringBootApplication -@EnableJpaRepositories -public class AzureWebDemoApplication { - - public static void main(String[] args) { - SpringApplication.run(AzureWebDemoApplication.class, args); - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/Country.java b/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/Country.java deleted file mode 100644 index 2fbecb4bd..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/Country.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.azure.web; - -import java.util.Objects; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.Table; - -/** - * - * @author Christian Tzolov - */ - -@Table(name = "COUNTRIES") -@Entity -public class Country { - - @Id - @GeneratedValue - private Long id; - - private String name; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - Country country = (Country) o; - return id == country.id && name.equals(country.name); - } - - @Override - public int hashCode() { - return Objects.hash(id, name); - } - - @Override - public String toString() { - return "Country{" + - "id=" + id + - ", name='" + name + '\'' + - '}'; - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/CountryController.java b/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/CountryController.java deleted file mode 100644 index f6d16f918..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/CountryController.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.azure.web; - -import java.util.stream.Collectors; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -/** - * - * @author Christian Tzolov - */ -@RestController -public class CountryController { - - @Autowired - private CountryRepository countryRepository; - - @GetMapping("/") - public String index() { - return "Country Count: " + countryRepository.count(); - } - - @GetMapping("/countries") - public String allCountries() { - String countries = this.countryRepository.findAll().stream() - .map(country -> country.toString()) - .collect(Collectors.joining()); - - return "Countries: " + countries; - } - - @PostMapping(path = "/countries") - public Country addCountry(@RequestBody Country country) { - if (!StringUtils.hasText(country.getName())) { - return null; - } - return this.countryRepository.save(country); - } - - @GetMapping("/countries/{id}") - public Country countryById(@PathVariable Integer id) { - return this.countryRepository.findById(id).get(); - } -} diff --git a/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/CountryRepository.java b/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/CountryRepository.java deleted file mode 100644 index 3f08e010b..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/src/main/java/com/example/azure/web/CountryRepository.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.azure.web; - -import org.springframework.data.jpa.repository.JpaRepository; - -/** - * - * @author Christian Tzolov - */ -public interface CountryRepository extends JpaRepository { -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-web/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-azure-web/src/main/resources/application.properties deleted file mode 100644 index 2226f0b85..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/src/main/resources/application.properties +++ /dev/null @@ -1,6 +0,0 @@ -spring.datasource.url=jdbc:h2:mem:testdb -spring.datasource.driverClassName=org.h2.Driver -spring.datasource.username=sa -spring.datasource.password=password -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect -spring.jpa.defer-datasource-initialization=true \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure-web/src/main/resources/host.json b/spring-cloud-function-samples/function-sample-azure-web/src/main/resources/host.json deleted file mode 100644 index 10d0c0748..000000000 --- a/spring-cloud-function-samples/function-sample-azure-web/src/main/resources/host.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "2.0", - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[3.*, 4.0.0)" - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-azure/.gitignore b/spring-cloud-function-samples/function-sample-azure/.gitignore deleted file mode 100644 index c7f1c2e74..000000000 --- a/spring-cloud-function-samples/function-sample-azure/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -extensions.csproj -obj/ -lib/ - diff --git a/spring-cloud-function-samples/function-sample-azure/Dockerfile b/spring-cloud-function-samples/function-sample-azure/Dockerfile deleted file mode 100644 index b8d1c0b63..000000000 --- a/spring-cloud-function-samples/function-sample-azure/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM springcloudstream/azure-functions-java17:1.0.0 - -COPY ./target/azure-functions /src/java-function-app - -RUN mkdir -p /home/site/wwwroot && \ - cd /src/java-function-app && \ - cd $(ls -d */|head -n 1) && \ - cp -a . /home/site/wwwroot - -ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ - AzureFunctionsJobHost__Logging__Console__IsEnabled=true diff --git a/spring-cloud-function-samples/function-sample-azure/README.adoc b/spring-cloud-function-samples/function-sample-azure/README.adoc deleted file mode 100644 index f9812e70d..000000000 --- a/spring-cloud-function-samples/function-sample-azure/README.adoc +++ /dev/null @@ -1,507 +0,0 @@ -== (Legacy) FunctionInvoker Integration - -WARNING: The legacy `FunctionInvoker` programming model is deprecated and will not be supported going forward. -For up to date samples, aligned with the dependency-injections https://docs.spring.io/spring-cloud-function/docs/current/reference/html/azure.html#_microsoft_azure[Azure Adapter], check the: https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-blob-trigger[Blob Trigger], https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-http-trigger[HTTP Trigger], https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-timer-trigger[Timer Trigger], https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-kafka-trigger[ Kafka Trigger & Output Binding]. -For a Gradle project example check the https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle[ HTTP Trigger with Gradle]. - -=== Overview -The https://azure.microsoft.com[Azure] adapter bootstraps a Spring Cloud Function context and channels function calls from the Azure -framework into the user functions, using Spring Boot configuration where necessary. Azure Functions has quite a unique and -invasive programming model, involving annotations in user code that are specific to the Azure platform. -However, it is important to understand that because of the style of integration provided by Spring Cloud Function, specifically `org.springframework.cloud.function.adapter.azure.FunctionInvoker`, this annotation-based programming model is simply a type-safe way to configure -your simple java function (function that has no awareness of Azure) to be recognized as Azure function. -All you need to do is create a handler that extends `FunctionInvoker`, define and configure your function handler method and -make a callback to `handleRequest(..)` method. This handler method provides input and output types as annotated method parameters -(enabling Azure to inspect the class and create JSON bindings). - - -[source,java] ----- -public class UppercaseHandler extends FunctionInvoker, String> { - - @FunctionName("uppercase") - public String execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET, - HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - Message message = MessageBuilder.withPayload(request.getBody().get()).copyHeaders(request.getHeaders()).build(); - return handleRequest(message, context); - } -} ----- - -Note that aside form providing configuration via Azure annotation we create an instance of `Message` inside the body of this handler method and make a callback to `handleRequest(..)` method returning its result. - -The actual user function you're delegating to looks like this - -[source,java] ----- -@Bean -public Function uppercase() { - return payload -> payload.toUpperCase(); -} - -OR - -@Bean -public Function, String> uppercase() { - return message -> message.getPayload().toUpperCase(); -} ----- - -Note that when creating a Message you can copy HTTP headers effectively making them available to you if necessary. - -The `org.springframework.cloud.function.adapter.azure.FunctionInvoker` class has two useful -methods (`handleRequest` and `handleOutput`) to which you can delegate the actual function call, so mostly the function will only ever have one line. - -The function name (definition) will be retrieved from Azure's `ExecutionContext.getFunctionName()` method, effectively supporting multiple function in the application context. - -==== Accessing Azure ExecutionContext - -Some time there is a need to access the target execution context provided by the Azure runtime in the form of `com.microsoft.azure.functions.ExecutionContext`. -For example one of such needs is logging, so it can appear in the Azure console. - -For that purpose the FunctionInvoker will add an instance of the `ExecutionContext` as a Message header so you can retrieve it via `executionContext` key. - -``` -@Bean -public Function, String> uppercase(JsonMapper mapper) { - return message -> { - String value = message.getPayload(); - ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext"); - . . . - } -} -``` - -==== Notes on JAR Layout - -You don't need the Spring Cloud Function Web at runtime in Azure, so you can exclude this -before you create the JAR you deploy to Azure, but it won't be used if you include it, so -it doesn't hurt to leave it in. A function application on Azure is an archive generated by - the Maven plugin. The function lives in the JAR file generated by this project. - The sample creates it as an executable jar, using the thin layout, so that Azure can find - the handler classes. If you prefer you can just use a regular flat JAR file. - The dependencies should *not* be included. - -==== Build file setup - -In order to run Spring Cloud Function applications on Microsoft Azure, you can leverage the Maven -plugin offered by the cloud platform provider. - -In order to use the adapter plugin for Maven, add the plugin dependency to your `pom.xml` -file: - -[source,xml] ----- - - - org.springframework.cloud - spring-cloud-function-adapter-azure - - ----- - -Then, configure the plugin. You will need to provide Azure-specific configuration for your -application, specifying the `resourceGroup`, `appName` and other optional properties, and - add the `package` goal execution so that the `function.json` file required by Azure is - generated for you. Full plugin documentation can be found in the https://github.com/microsoft/azure-maven-plugins[plugin repository]. - -[source,xml] ----- - - com.microsoft.azure - azure-functions-maven-plugin - - ${functionResourceGroup} - ${functionAppName} - - - - package-functions - - package - - - - ----- - -You will also have to ensure that the files to be scanned by the plugin can be found in the -Azure functions staging directory (see the https://github.com/microsoft/azure-maven-plugins[plugin repository] - for more details on the staging directory and it's default location). - -You can find the entire sample `pom.xml` file for deploying Spring Cloud Function -applications to Microsoft Azure with Maven https://github.com/spring-cloud/spring-cloud-function/blob/{branch}/spring-cloud-function-samples/function-sample-azure/pom.xml[here]. - -NOTE: As of yet, only Maven plugin is available. Gradle plugin has not been created by -the cloud platform provider. - -==== Build - ----- -./mvnw -U clean package ----- - -==== Running the sample - -You can run the sample locally, just like the other Spring Cloud Function samples: - ---- -./mvnw spring-boot:run ---- - -and `curl -H "Content-Type: text/plain" localhost:8080/api/uppercase -d '{"value": "hello foobar"}'`. - -You will need the `az` CLI app (see https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven for more detail). To deploy the function on Azure runtime: - ----- -$ az login -$ mvn azure-functions:deploy ----- - -On another terminal try this: `curl https:///api/uppercase -d '{"value": "hello foobar!"}'`. Please ensure that you use the right URL for the function above. Alternatively you can test the function in the Azure Dashboard UI (click on the function name, go to the right hand side and click "Test" and to the bottom right, "Run"). - -The input type for the function in the Azure sample is a Foo with a single property called "value". So you need this to test it with something like below: - ----- -{ - "value": "foobar" -} ----- - -NOTE: The Azure sample app is written in the "non-functional" style (using `@Bean`). The functional style (with just `Function` or `ApplicationContextInitializer`) is much faster on startup in Azure than the traditional `@Bean` style, so if you don't need `@Beans` (or `@EnableAutoConfiguration`) it's a good choice. Warm starts are not affected. - - -== Running Sample Locally -You can run this Azure function locally, similar to other Spring Cloud Function samples, however -this time by using the Azure Maven plugin, as the Microsoft Azure functions execution context must be available. - -NOTE: To run locally on top of Azure Functions, and to deploy to your live Azure environment, you will need the Azure Functions Core Tools installed along with the Azure CLI (see https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser#configure-your-local-environment[here] for details). - -.Follow these steps to build and run locally: -[source,bash] ----- -../../mvnw clean package -../../mvnw azure-functions:run ----- -.console output -[source,bash] ----- -[INFO] Azure Function App's staging directory found at: /Users/cbono/repos/spring-cloud-function/spring-cloud-function-samples/function-sample-azure/target/azure-functions/spring-cloud-function-samples -4.0.3971 -[INFO] Azure Functions Core Tools found. - -Azure Functions Core Tools -Core Tools Version: 4.0.3971 Commit hash: d0775d487c93ebd49e9c1166d5c3c01f3c76eaaf (64-bit) -Function Runtime Version: 4.0.1.16815 - -info: Microsoft.AspNetCore.Hosting.Diagnostics[1] - Request starting HTTP/2 POST http://127.0.0.1:53836/AzureFunctionsRpcMessages.FunctionRpc/EventStream application/grpc - -info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] - Executing endpoint 'gRPC - /AzureFunctionsRpcMessages.FunctionRpc/EventStream' -[2022-04-11T03:04:05.143Z] OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release. -[2022-04-11T03:04:05.247Z] Worker process started and initialized. - -Functions: - - echo: [GET,POST] http://localhost:7071/api/echo - - echoStream: [GET,POST] http://localhost:7071/api/echoStream - - uppercase: [GET,POST] http://localhost:7071/api/uppercase - - uppercaseReactive: [GET,POST] http://localhost:7071/api/uppercaseReactive - -For detailed output, run func with --verbose flag. -[2022-04-11T03:04:10.163Z] Host lock lease acquired by instance ID '000000000000000000000000BEFE21CF'. - ----- - -.Test the _uppercase_ function using the following _curl_ command: -[source,bash] ----- -curl -H "Content-Type: application/json" localhost:7071/api/uppercase -d '{"greeting": "hello", "name": "foo"}' ----- -.curl response -[source,json] ----- -{ - "greeting": "HELLO", - "name": "FOO" -} ----- -Notice that the URL is of the format `/api/`). - -The `uppercase` function signature is `Function, String> uppercase()`. The implementation of `UppercaseHandler` (which extends `FunctionInvoker`) copies the HTTP headers of the incoming request into the input message's _MessageHeaders_ which makes them accessible to the function if needed. - -NOTE: Implementation of `FunctionInvoker` (your handler), should contain the least amount of code. It is really a type-safe way to define -and configure function to be recognized as Azure Function. -Everything else should be delegated to the base `FunctionInvoker` via `handleRequest(..)` callback which will invoke your function, taking care of -necessary type conversion, transformation etc. One exception to this rule is when custom result handling is required. In that case, the proper post-process method can be overridden as well in order to take control of the results processing. - -.UppercaseHandler.java -[source,java] ----- -@FunctionName("uppercase") -public String execute( - @HttpTrigger( - name = "req", - methods = {HttpMethod.GET, HttpMethod.POST}, - authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context -) { - Message message = MessageBuilder.withPayload(request.getBody().get()) - .copyHeaders(request.getHeaders()).build(); - return handleRequest(message, context); -} ----- - - -The `echo` function does the same as the `uppercase` less the actual uppercasing. However, the important difference to notice is that function itself -takes primitive `String` as its input (i.e., `public Function echo()`) while the actual handler passes instance of `Message` the same way as with `uppercase`. The framework recognizes that you only care about the payload and extracts it from the `Message` before calling the function. - -There is also a reactive version of _uppercase_ (named _uppercaseReactive_) which will produce the same result, but -demonstrates and validates the ability to use reactive functions with Azure. - -== Running on Azure - -NOTE: The Azure Java functions runtime does not yet support Java 17 but Spring Cloud Function 4.x requires it. To get around this limitation we deploy to Azure in a custom Docker container. Once https://github.com/Azure/azure-functions-java-worker/issues/548[Azure supports] Java 17 we can move back to using non-Docker deployments. - -==== Custom Docker Image -The steps below describe the process to create a custom Docker image which is suitable for deployment on Azure and contains the 4.x Azure Functions runtime, the MS Java 17 JVM, and the sample functions in this repo. - -====== Image name -Pick an image name for the Docker container (eg. `onobc/function-sample-azure-java17:1.0.0`) and update the _pom.xml_ `functionDockerImageName` property with the image name. - -TIP: By default it is expected that the image name is a publicly accessible image on Docker Hub. However, other registries and credentials can be configured as described https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supporte-runtime[here]. - -.Rebuild the functions (pom.xml was updated): -[source,bash] ----- -../../mvnw clean package ----- -.Build the Docker image: -[source,bash] ----- -docker build -t . ----- - -Test the Docker image locally by starting the container and issuing a request. - -.Start the function runtime locally in Docker: -[source,bash] ----- -docker run -p 8080:80 ----- - -.console output -[source,bash] ----- -cbono@cbono-a01 function-sample-azure % docker run -p 8080:80 onobc/function-sample-azure-java17:1.0.0 -info: Host.Triggers.Warmup[0] - Initializing Warmup Extension. -info: Host.Startup[503] - Initializing Host. OperationId: 'e7317c18-4daa-4d69-bf38-beaa51e1a012'. -info: Host.Startup[504] - Host initialization: ConsecutiveErrors=0, StartupCount=1, OperationId=e7317c18-4daa-4d69-bf38-beaa51e1a012 -info: Microsoft.Azure.WebJobs.Hosting.OptionsLoggingService[0] - LoggerFilterOptions - { - "MinLevel": "None", - "Rules": [ - { - "ProviderName": null, - "CategoryName": null, - "LogLevel": null, - "Filter": "b__0" - }, - { - "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider", - "CategoryName": null, - "LogLevel": "None", - "Filter": null - }, - { - "ProviderName": "Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.SystemLoggerProvider", - "CategoryName": null, - "LogLevel": null, - "Filter": "b__0" - } - ] - } -... -... -... -info: Microsoft.Azure.WebJobs.Script.WebHost.WebScriptHostHttpRoutesManager[0] - Initializing function HTTP routes - Mapped function route 'api/echo' [GET,POST] to 'echo' - Mapped function route 'api/echoStream' [GET,POST] to 'echoStream' - Mapped function route 'api/uppercase' [GET,POST] to 'uppercase' - Mapped function route 'api/uppercaseReactive' [GET,POST] to 'uppercaseReactive' - -info: Host.Startup[412] - Host initialized (65ms) -info: Host.Startup[413] - Host started (81ms) -info: Host.Startup[0] - Job host started -Hosting environment: Production -Content root path: /azure-functions-host -Now listening on: http://[::]:80 -Application started. Press Ctrl+C to shut down. -info: Microsoft.Azure.WebJobs.Script.Workers.Rpc.RpcFunctionInvocationDispatcher[0] - Worker process started and initialized. -info: Host.General[337] - Host lock lease acquired by instance ID '000000000000000000000000C4043012'. ----- - -.Test the _uppercase_ function using the following _curl_ command: -[source,bash] ----- -curl -H "Content-Type: application/json" localhost:8080/api/uppercase -d '{"greeting": "hello", "name": "foo"}' ----- -.curl response -[source,json] ----- -{ - "greeting": "HELLO", - "name": "FOO" -} ----- - -.Push the image to Docker registry: -[source,bash] ----- -docker push ----- -At this point the custom image has been created and pushed to the configured Docker registry. - -==== Deploy to Azure -To deploy the functions to your live Azure environment, including automatic provisioning of an _HTTPTrigger_ for each function, do the following. - -.Login to Azure: -[source,bash] ----- -az login ----- - -.Deploy to Azure: -[source,bash] ----- -../../mvnw azure-functions:deploy ----- -.console output -[source,bash] ----- -[INFO] ---------------< io.spring.sample:function-sample-azure >--------------- -[INFO] Building function-sample-azure 4.0.0.RELEASE -[INFO] --------------------------------[ jar ]--------------------------------- -[INFO] -[INFO] --- azure-functions-maven-plugin:1.16.0:deploy (default-cli) @ function-sample-azure --- -Auth type: AZURE_CLI -Default subscription: SCDF-Azure(b80d18******) -Username: cbono@vmware.com -[INFO] Subscription: SCDF-Azure(*******) -[INFO] Reflections took 123 ms to scan 6 urls, producing 24 keys and 486 values -[INFO] Start creating Resource Group(java-functions-group) in region (West US)... -[INFO] Resource Group(java-functions-group) is successfully created. -[INFO] Reflections took 1 ms to scan 3 urls, producing 12 keys and 12 values -[INFO] Creating app service plan java-functions-app-service-plan... -[INFO] Successfully created app service plan java-functions-app-service-plan. -[INFO] Start creating Application Insight (spring-cloud-function-samples)... -[INFO] Application Insight (spring-cloud-function-samples) is successfully created. You can visit https://ms.portal.azure.com/********providers/Microsoft.Insights/components/spring-cloud-function-samples to view your Application Insights component. -[INFO] Creating function app spring-cloud-function-samples... -[INFO] Set function worker runtime to java. -[INFO] Ignoring decoding of null or empty value to:com.azure.resourcemanager.storage.fluent.models.StorageAccountInner -[INFO] Successfully created function app spring-cloud-function-samples. -[INFO] Skip deployment for docker app service -[INFO] ------------------------------------------------------------------------ -[INFO] BUILD SUCCESS -[INFO] ------------------------------------------------------------------------ -[INFO] Total time: 01:30 min -[INFO] Finished at: 2022-04-04T19:06:24-05:00 -[INFO] ------------------------------------------------------------------------ ----- - -TIP: When deployed as a Docker container the function urls are not written to the console. You will need to inspect the functions in the Azure Portal to find the urls. - -==== Inspect in Azure Portal - -Navigate to the https://portal.azure.com/#blade/HubsExtension/BrowseResource/resourceType/Microsoft.Web%2Fsites/kind/functionapp[Function App] dashboard in the Azure portal and then: - -* click on your function app (`"spring-cloud-function-samples"` by default) -* click the left nav `"Functions"` link -* click the `"uppercase"` function - -====== Function Url -Click the `"Get Function Url"` link to see the function's url. - -====== Test via Portal -* click on the left nav `"Code and Test"` -* click on `"Test/Run"` at top of page -* enter the following input json in the `"Body"` section on the right-hand side: - -[source,json] ----- -{ - "greeting": "hello", - "name": "foo" -} ----- -* click "Run" and the output should look like: - -[source,json] ----- -{ - "greeting": "HELLO", - "name": "FOO" -} ----- - -===== Test via cURL -Armed w/ the function url from above, issue the following curl command in another terminal: - -[source,bash] ----- -curl -H "Content-Type: application/json" https://spring-cloud-function-samples.azurewebsites.net/api/uppercase -d '{"greeting": "hello", "name": "foo"}' ----- -.curl response -[source,json] ----- -{ - "greeting": "HELLO", - "name": "FOO" -} ----- - -TIP: The Azure dashboard provides a plethora of information about your functions, including but not limited to execution count, memory consumption and execution time. - - -==== Custom Result Handling - -As noted above, the implementation of `FunctionInvoker` (your handler), should contain the least amount of code possible. However, if custom result handling needs to occur there is a set of methods (named `postProcess**`) that can be overridden in link:../../spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java[FunctionInvoker.java]. - -One such example can be seen in link:src/main/java/example/ReactiveEchoCustomResultHandler.java[ReactiveEchoCustomResultHandler.java]. - -Once the function is deployed it can be tested using _curl_: - -[source,bash] ----- -curl -H "Content-Type: application/json" localhost:7071/api/echoStream -d '["hello","peepz"]' ----- -.result -[source,bash] ----- -Kicked off job for [hello, peepz] ----- -The custom result handling takes the Flux returned from the `echoStream` function and adds logging, uppercase mapping, and then subscribes to the publisher. The Azure logs output the following: - -[source,bash] ----- -[2022-03-01T01:36:57.439Z] 2022-02-28 19:36:57.439 INFO 20587 --- [pool-2-thread-2] o.s.boot.SpringApplication : Started application in 0.466 seconds (JVM running for 57.906) -[2022-03-01T01:36:57.462Z] BEGIN echo post-processing work ... -[2022-03-01T01:36:57.462Z] HELLO -[2022-03-01T01:36:57.462Z] PEEPZ -[2022-03-01T01:36:57.463Z] END echo post-processing work -[2022-03-01T01:36:57.463Z] Function "echoStream" (Id: 678cff0b-d958-4fab-967b-e19e0d5d67e8) invoked by Java Worker ----- diff --git a/spring-cloud-function-samples/function-sample-azure/pom.xml b/spring-cloud-function-samples/function-sample-azure/pom.xml deleted file mode 100644 index bd9376697..000000000 --- a/spring-cloud-function-samples/function-sample-azure/pom.xml +++ /dev/null @@ -1,266 +0,0 @@ - - - 4.0.0 - - io.spring.sample - function-sample-azure - 0.0.1-SNAPSHOT - jar - - function-sample-azure - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - spring-cloud-function-samples - westus - java-functions-group - java-functions-app-service-plan - onobc/function-sample-azure-java17:1.0.0 - ${project.build.directory}/azure-functions/${functionAppName} - example.Config - 1.21.0 - 2.1.0 - 1.0.27.RELEASE - 17 - UTF-8 - - - - - org.springframework.cloud - spring-cloud-function-adapter-azure - 4.1.0-SNAPSHOT - - - org.springframework.cloud - spring-cloud-starter-function-web - provided - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - 4.1.0-SNAPSHOT - pom - import - - - com.microsoft.azure.functions - azure-functions-java-library - ${azure.functions.java.library.version} - - - - - - - - - org.apache.maven.plugins - maven-clean-plugin - - - - ${basedir} - - obj/** - - - - ${basedir} - - extensions.csproj - - - - - - - com.microsoft.azure - azure-functions-maven-plugin - ${azure.functions.maven.plugin.version} - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-dependencies - prepare-package - - copy-dependencies - - - ${stagingDirectory}/lib - false - false - true - runtime - azure-functions-java-library - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - ${java.version} - ${java.version} - ${project.build.sourceEncoding} - - - - com.microsoft.azure - azure-functions-maven-plugin - - ${functionResourceGroup} - ${functionAppName} - ${functionAppRegion} - ${functionAppServicePlanName} - EP1 - - - linux - 17 - - - - - FUNCTIONS_EXTENSION_VERSION - ~4 - - - - - - package-functions - - package - - - - - - maven-resources-plugin - - - copy-resources - package - - copy-resources - - - true - - ${stagingDirectory} - - - - ${project.basedir}/src/main/azure - - - ** - - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/azure/host.json b/spring-cloud-function-samples/function-sample-azure/src/main/azure/host.json deleted file mode 100644 index cfb16c7ce..000000000 --- a/spring-cloud-function-samples/function-sample-azure/src/main/azure/host.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "functionTimeout": "00:05:00", - "version": "2.0" -} diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/azure/local.settings.json b/spring-cloud-function-samples/function-sample-azure/src/main/azure/local.settings.json deleted file mode 100644 index 3470fde9a..000000000 --- a/spring-cloud-function-samples/function-sample-azure/src/main/azure/local.settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "", - "AzureWebJobsDashboard": "", - "FUNCTIONS_WORKER_RUNTIME": "java" - } -} diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java deleted file mode 100644 index b40249436..000000000 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import java.util.Map; -import java.util.function.Function; - -import com.microsoft.azure.functions.ExecutionContext; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; - -/** - * @author Oleg Zhurakousky - * @author Chris Bono - */ -@SpringBootApplication -public class Config { - - public static void main(String[] args) throws Exception { - SpringApplication.run(Config.class, args); - } - - @Bean - public Function echo() { - return payload -> payload; - } - - @Bean - public Function, String> uppercase(JsonMapper mapper) { - return message -> { - String value = message.getPayload(); - ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext"); - try { - Map map = mapper.fromJson(value, Map.class); - - if(map != null) - map.forEach((k, v) -> map.put(k, v != null ? v.toUpperCase() : null)); - - if(context != null) - context.getLogger().info(new StringBuilder().append("Function: ") - .append(context.getFunctionName()).append(" is uppercasing ").append(value.toString()).toString()); - - return mapper.toString(map); - } catch (Exception e) { - e.printStackTrace(); - if(context != null) - context.getLogger().severe("Function could not parse incoming request"); - - return ("Function error: - bad request"); - } - }; - } - - @Bean - public Function, Mono> uppercaseReactive() { - return mono -> mono.map(value -> value.toUpperCase()); - } - - @Bean - public Function, Flux> echoStream() { - return flux -> flux.map(value -> value.toUpperCase()); - } -} - diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/EchoHandler.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/EchoHandler.java deleted file mode 100644 index 784f0e86c..000000000 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/EchoHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; - -import java.util.Optional; - -import org.springframework.cloud.function.adapter.azure.FunctionInvoker; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -/** - * @author Soby Chacko - * @author Oleg Zhurakousky - */ -public class EchoHandler extends FunctionInvoker, String> { - - @FunctionName("echo") - public String execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET, - HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - Message message = MessageBuilder.withPayload(request.getBody().get()).copyHeaders(request.getHeaders()).build(); - return handleRequest(message, context); - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveEchoCustomResultHandler.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveEchoCustomResultHandler.java deleted file mode 100644 index 8f72a80bc..000000000 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveEchoCustomResultHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import java.util.List; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import reactor.core.publisher.Flux; - -import org.springframework.cloud.function.adapter.azure.FunctionInvoker; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; - -/** - * Sample that shows how to customize the default function result handling by operating on the {@link Flux} returned - * from the {@link Config#echoStream()} echoStream} function. - * - * @author Chris Bono - */ -public class ReactiveEchoCustomResultHandler extends FunctionInvoker, String> { - - private static final Log logger = LogFactory.getLog(ReactiveEchoCustomResultHandler.class); - - @FunctionName("echoStream") - public String execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) - HttpRequestMessage> request, ExecutionContext context - ) { - return handleRequest(request.getBody(), context); - } - - @Override - protected String postProcessFluxFunctionResult(List rawInputs, Object functionInputs, Flux functionResult, - FunctionInvocationWrapper function, ExecutionContext executionContext - ) { - functionResult - .doFirst(() -> executionContext.getLogger().info("BEGIN echo post-processing work ...")) - .mapNotNull((v) -> v.toString().toUpperCase()) - .doFinally((signalType) -> executionContext.getLogger().info("END echo post-processing work")) - .subscribe((v) -> executionContext.getLogger().info(" " + v)); - return "Kicked off job for " + rawInputs; - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveUppercaseHandler.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveUppercaseHandler.java deleted file mode 100644 index cb82087b5..000000000 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveUppercaseHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; - -import java.util.Optional; - -import org.springframework.cloud.function.adapter.azure.FunctionInvoker; - -/** - * @author Oleg Zhurakousky - */ -public class ReactiveUppercaseHandler extends FunctionInvoker { - - @FunctionName("uppercaseReactive") - public String execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET, - HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context) { - return handleRequest(request.getBody().get(), context); - } - -} diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/UppercaseHandler.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/UppercaseHandler.java deleted file mode 100644 index 0e342e960..000000000 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/UppercaseHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2018-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; - -import java.util.Optional; - -import org.springframework.cloud.function.adapter.azure.FunctionInvoker; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -/** - * @author Soby Chacko - * @author Oleg Zhurakousky - */ -public class UppercaseHandler extends FunctionInvoker, String> { - - @FunctionName("uppercase") - public String execute( - @HttpTrigger( - name = "req", - methods = {HttpMethod.GET, HttpMethod.POST}, - authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - ExecutionContext context - ) { - context.getLogger().warning("Using Java (" + System.getProperty("java.version") + ")"); - Message message = MessageBuilder.withPayload(request.getBody().get()) - .copyHeaders(request.getHeaders()).build(); - return handleRequest(message, context); - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.gitignore b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index e76d1f324..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55..000000000 Binary files a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 642d572ce..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/README.adoc b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/README.adoc deleted file mode 100644 index ba154a464..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/README.adoc +++ /dev/null @@ -1,61 +0,0 @@ -## Examples of Cloud Events with Spring via RSocket and Apache Kafka - -### Introduction -The current example uses spring-cloud-function framework as its core which allows users to only worry about functional aspects of -their requirement while taking care-off non-functional aspects. For more information on Spring Cloud Function please visit -our https://spring.io/projects/spring-cloud-function[project page]. - -The example consists of a Spring boot configuration class -https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/DemoApplication.java[DemoApplication] -which contains a sample function which you can interact with following via RSocket and Apache Kafka. - -### From RSocket to Apache Kafka - -While very similar to https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-cloudevent-stream[spring-cloud-function-stream] example -there are few interesting variants here worth discussing. -Here we’re introducing a different delivery mechanism. But what really makes it even more interesting is the fact that unlike Apache Kafka or AMQP there is no protocol -binding defined for RSocket. So we will communicate Cloud Event in a structured-mode where the entire event is encoded into some type of structure (e.g., JSON). - -Few implementation details are also defer in this example from the others. However these details are not relevant in any way to Cloud Event, rather -demonstration of other mechanisms you may chose to write your code. For example we’ll be using `Consumer` instead of a `Function` and will be manually -sending an output message using `StreamBridge` component provided by Spring Cloud Stream framework. - -So, here is our application code - -``` -@Bean -public Consumer hire(StreamBridge streamBridge) { - return person -> { - Employee employee = new Employee(person); - streamBridge.send("hire-out-0", CloudEventMessageBuilder.withData(employee) - .setSource("http://spring.io/rsocket") - .setId("1234567890") - .build()); - }; -} -``` -Note how we’re utiliziing CloudEventMessageBuilder to generate output Message as Cloud Event. - -What we will be sending over RSocket is structured representation of Cloud Event: -``` -String payload = "{\n" + - " \"specversion\" : \"1.0\",\n" + - " \"type\" : \"org.springframework\",\n" + - " \"source\" : \"https://spring.io/\",\n" + - " \"id\" : \"A234-1234-1234\",\n" + - " \"datacontenttype\" : \"application/json\",\n" + - " \"data\" : {\n" + - " \"firstName\" : \"John\",\n" + - " \"lastName\" : \"Doe\"\n" + - " }\n" + - "}"; -``` -So, the entire Cloud Event is represented as JSON sent over RSocket to the hire() function. - -``` -rsocketRequesterBuilder.tcp("localhost", 55555) - .route("hire") // target function - .data(payload). // data we're sending - .send() -``` -You can run the demo using https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/test/java/io/spring/cloudevent/DemoApplicationTests.java[DemoApplicationTests] \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/mvnw b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/mvnw deleted file mode 100755 index a16b5431b..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/mvnw.cmd b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/mvnw.cmd deleted file mode 100644 index c8d43372c..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/pom.xml b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/pom.xml deleted file mode 100644 index 59bed8335..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/pom.xml +++ /dev/null @@ -1,166 +0,0 @@ - - - 4.0.0 - io.spring.sample - function-sample-cloudevent-rsocket - 0.0.1-SNAPSHOT - function-sample-cloudevent-rsocket - Demo project for Spring Boot - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - - - org.springframework.cloud - spring-cloud-function-rsocket - - - - - - - org.springframework.cloud - spring-cloud-stream-binder-kafka - 4.1.0-SNAPSHOT - - - - io.projectreactor - reactor-test - test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - maven-surefire-plugin - - - **/*Tests.java - **/*Test.java - - - **/Abstract*.java - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - - diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/DemoApplication.java b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/DemoApplication.java deleted file mode 100644 index a42bf0b79..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/DemoApplication.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.spring.cloudevent; - -import java.util.function.Consumer; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.cloudevent.CloudEventMessageBuilder; -import org.springframework.cloud.stream.function.StreamBridge; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class DemoApplication { - - public static void main(String[] args) throws Exception { - SpringApplication.run(DemoApplication.class, args); - } - - @Bean - public Consumer hire(StreamBridge streamBridge) { - return person -> { - Employee employee = new Employee(person); - streamBridge.send("hire-out-0", CloudEventMessageBuilder.withData(employee) - .setSource("http://spring.io/rsocket") - .setId("1234567890") - .build()); - }; - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/Employee.java b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/Employee.java deleted file mode 100644 index e1f04615e..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/Employee.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.spring.cloudevent; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Random; - -public class Employee { - - private Person person; - - private int id; - - public Employee() { - - } - - public Employee(Person person) { - this.person = person; - this.id = new Random().nextInt(1000); - } - - public Person getPerson() { - return person; - } - - public void setPerson(Person person) { - this.person = person; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getMessage() { - return "Employee " + id + " was hired on " + new SimpleDateFormat("dd-MM-yyyy").format(new Date()); - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/Person.java b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/Person.java deleted file mode 100644 index 99ded7514..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/java/io/spring/cloudevent/Person.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.spring.cloudevent; - -public class Person { - - private String firstName; - - private String lastName; - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/main/resources/application.properties deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/test/java/io/spring/cloudevent/DemoApplicationTests.java b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/test/java/io/spring/cloudevent/DemoApplicationTests.java deleted file mode 100644 index 87e232a57..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/src/test/java/io/spring/cloudevent/DemoApplicationTests.java +++ /dev/null @@ -1,83 +0,0 @@ -package io.spring.cloudevent; - -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.Collections; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.TimeUnit; - -import org.apache.kafka.clients.admin.KafkaAdminClient; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ConditionEvaluationResult; -import org.junit.jupiter.api.extension.ExecutionCondition; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils; -import org.springframework.kafka.annotation.KafkaListener; -import org.springframework.messaging.Message; -import org.springframework.messaging.rsocket.RSocketRequester; -import org.springframework.util.MimeTypeUtils; - - -@SpringBootTest(properties = {"spring.rsocket.server.port=55551"}) -@ExtendWith(DemoApplicationTests.TestRule.class) -public class DemoApplicationTests { - - ArrayBlockingQueue> queue = new ArrayBlockingQueue<>(1000); - - @Autowired - private RSocketRequester.Builder rsocketRequesterBuilder; - - @Test - public void test() throws Exception { - String payload = "{\n" + - " \"specversion\" : \"1.0\",\n" + - " \"type\" : \"org.springframework\",\n" + - " \"source\" : \"https://spring.io/\",\n" + - " \"id\" : \"A234-1234-1234\",\n" + - " \"datacontenttype\" : \"application/json\",\n" + - " \"data\" : {\n" + - " \"firstName\" : \"John\",\n" + - " \"lastName\" : \"Doe\"\n" + - " }\n" + - "}"; - - this.rsocketRequesterBuilder.tcp("localhost", 55551) - .route("hire") - .metadata("{\"content-type\":\"application/cloudevents+json\"}", MimeTypeUtils.APPLICATION_JSON) - .data(payload) - .send() - .subscribe(); - - Message resultFromKafka = queue.poll(2000, TimeUnit.MILLISECONDS); - System.out.println("Result Message: " + resultFromKafka); - System.out.println("Cloud Event 'specversion': " + CloudEventMessageUtils.getSpecVersion(resultFromKafka)); - System.out.println("Cloud Event 'source': " + CloudEventMessageUtils.getSource(resultFromKafka)); - System.out.println("Cloud Event 'id': " + CloudEventMessageUtils.getId(resultFromKafka)); - System.out.println("Cloud Event 'type': " + CloudEventMessageUtils.getType(resultFromKafka)); - } - - @KafkaListener(id = "test", topics = "hire-out-0", clientIdPrefix = "cloudEvents") - public void listen(Message message) { - queue.add(message); - } - - public static class TestRule implements ExecutionCondition { - @Override - public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { - try { - Socket socket = new Socket(); - socket.connect(new InetSocketAddress("localhost", 9092)); - socket.close(); - } - catch (Exception e) { - System.out.println("Kafka is not available on localhost:9092"); - return ConditionEvaluationResult.disabled("Kafka is not available on localhost, default port"); - } - - return ConditionEvaluationResult.enabled("All is good"); - } - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/.gitignore b/spring-cloud-function-samples/function-sample-cloudevent-sdk/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-sdk/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud-function-samples/function-sample-cloudevent-sdk/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index e76d1f324..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-sdk/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-cloudevent-sdk/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55..000000000 Binary files a/spring-cloud-function-samples/function-sample-cloudevent-sdk/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-cloudevent-sdk/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 642d572ce..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-sdk/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/README.adoc b/spring-cloud-function-samples/function-sample-cloudevent-sdk/README.adoc deleted file mode 100644 index fb2f81bcb..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-sdk/README.adoc +++ /dev/null @@ -1,25 +0,0 @@ -## Examples of integration with Cloud-Event SDK - -### Introduction -As described in these two blog posts (https://spring.io/blog/2020/12/10/cloud-events-and-spring-part-1 & https://spring.io/blog/2020/12/23/cloud-events-and-spring-part-2), -Spring natively supports Cloud Event specification so to work with Cloud Events you don't need anything and `function-sample-cloudevent`, `function-sample-cloudevent-rsocket` and `function-sample-cloudevent-stream` examples show exactly how you can benefit from Spring's transparent handling of Cloud Events. - -So this example shows integration of Spring with https://github.com/cloudevents/sdk-java[Cloud Event SDK] (not managed by Spring). - -NOTE: This is only to demonstrate the integration capabilities with Cloud Event SDK (regardless of the reason). It is NOT required to work with Cloud Events. - -In this example the `echo` function uses `CloudEvent` type from java SDK to receive Cloud Event and echo it back using `CloudEventBuilder`. -Note that you also need to register `CloudEventMessageConverter` - - -Simply start the `DemoApplication`. Once started access the `echo` function exposed as an HTTP endpoint - -``` -curl -v -d '{"value": "Foo"}' \ - -H'Content-type: application/json' \ - -H'ce-id: 1' \ - -H'ce-source: function-sample-cloudevent-sdk' \ - -H'ce-type: my.application.Foo' \ - -H'ce-specversion: 1.0' \ - http://localhost:8080/echo -``` \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/mvnw b/spring-cloud-function-samples/function-sample-cloudevent-sdk/mvnw deleted file mode 100755 index a16b5431b..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-sdk/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/mvnw.cmd b/spring-cloud-function-samples/function-sample-cloudevent-sdk/mvnw.cmd deleted file mode 100644 index c8d43372c..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-sdk/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/pom.xml b/spring-cloud-function-samples/function-sample-cloudevent-sdk/pom.xml deleted file mode 100644 index 0db1f2d84..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-sdk/pom.xml +++ /dev/null @@ -1,159 +0,0 @@ - - - 4.0.0 - io.spring.sample - function-sample-cloudevent-sdk - 0.0.1-SNAPSHOT - function-sample-cloudevent-rsocket - Demo project for Spring Boot - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.cloud - spring-cloud-function-web - - - org.springframework.boot - spring-boot-starter-web - - - io.cloudevents - cloudevents-spring - 2.3.0 - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - maven-surefire-plugin - - - **/*Tests.java - **/*Test.java - - - **/Abstract*.java - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - - diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/src/main/java/io/spring/cloudevent/DemoApplication.java b/spring-cloud-function-samples/function-sample-cloudevent-sdk/src/main/java/io/spring/cloudevent/DemoApplication.java deleted file mode 100644 index d7b4822bf..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-sdk/src/main/java/io/spring/cloudevent/DemoApplication.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.spring.cloudevent; - -import java.net.URI; -import java.util.UUID; -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -import io.cloudevents.CloudEvent; -import io.cloudevents.core.builder.CloudEventBuilder; -import io.cloudevents.spring.messaging.CloudEventMessageConverter; - -@SpringBootApplication -public class DemoApplication { - - public static void main(String[] args) throws Exception { - SpringApplication.run(DemoApplication.class, args); - } - - @Bean - public Function echo() { - return ce -> { - System.out.println("Received: " + ce); - return CloudEventBuilder.from(ce) - .withId(UUID.randomUUID().toString()) - .withSource(URI.create("https://spring.io/foos")) - .withType("io.spring.event.Foo") - .withData(ce.getData().toBytes()) - .build(); - }; - } - - @Bean - public CloudEventMessageConverter cloudEventMessageConverter() { - return new CloudEventMessageConverter(); - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-cloudevent-sdk/src/main/resources/application.properties deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/.gitignore b/spring-cloud-function-samples/function-sample-cloudevent-stream/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud-function-samples/function-sample-cloudevent-stream/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index e76d1f324..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-cloudevent-stream/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55..000000000 Binary files a/spring-cloud-function-samples/function-sample-cloudevent-stream/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-cloudevent-stream/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 642d572ce..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/README.adoc b/spring-cloud-function-samples/function-sample-cloudevent-stream/README.adoc deleted file mode 100644 index 1ca0bad56..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/README.adoc +++ /dev/null @@ -1,54 +0,0 @@ -## Examples of Cloud Events with Spring via AMQP and Apache Kafka - -### Introduction -The current example uses spring-cloud-function framework as its core which allows users to only worry about functional aspects of -their requirement while taking care-off non-functional aspects. For more information on Spring Cloud Function please visit -our https://spring.io/projects/spring-cloud-function[project page]. - -The example consists of a Spring boot configuration class -https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/DemoApplication.java[DemoApplication] -which contains a sample function which you can interact with following via AMQP and Apache Kafka. - -### From RabbitMQ to Apache Kafka - -Assuming you have RabbitMQ and Kafka running, start the application and send a Message to RabbitMQ. - -We included a https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample-cloudevent-stream/src/test/java/io/spring/cloudevent/DemoApplicationTests.java[demo test case] which effectively automates this demo by sending Cloud Event to RabbitMQ and receives one from Apache Kafka. - -``` -Message messageToAMQP = CloudEventMessageBuilder - .withData("{\"firstName\":\"John\", \"lastName\":\"Doe\"}".getBytes()) - .setSource("https://cloudevent.demo") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(CloudEventMessageUtils.AMQP_ATTR_PREFIX); - -rabbitTemplate.send("hire-in-0", "#", messageToAMQP); -Message resultFromKafka = queue.poll(2000, TimeUnit.MILLISECONDS); -System.out.println("Result Message: " + resultFromKafka); -. . . -``` - -Note how we are using CloudEventMessageBuilder here to only set source as Cloud Event attribute while relying on default values for the rest of the -required Cloud Event attributes. We’re also using build(CloudEventMessageUtils.AMQP_ATTR_PREFIX) to ensure that the attributes are prefixed with `cloudEvents:` -prefix (see Cloud Events AMQP protocol bindings). -Also note that on the receiving end Cloud Events attributes are now prefixed with `ce_` prefix (see Cloud Events Kafka protocol bindings), -since it was determined by the framework that the target destination is Apache Kafka. -This last point is worth elaborating a bit. We already established that setting Cloud Event attributes is a non-functional aspect and because -of it we’ve exposed a mechanism for you to deal with it outside of your business logic. But what about attribute prefixes? Note that we are running the -same code in different execution contexts. This means that the attribute prefixes actually depend on the execution context. So by being aware of the execution -context, the framework ensures the correctness of the Cloud Event attribute prefixes. - -You can also use http://localhost:15672/[RabbitMQ dashboard] (if you have it installed) and send message to `hire-in-0` exchange. -To stay compliant with Cloud Event specification you should provide attributes with AMQP appropriate prefixes (i.e., `cloudEvents:`). For example: - -``` -cloudEvents:specversion=1.0 -cloudEvents:type=hire -cloudEvents:source:spring.io/spring-event -cloudEvents:id=0001 -``` - -And your data: -``` -{"firstName":"John", "lastName":"Doe"} -``` diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/mvnw b/spring-cloud-function-samples/function-sample-cloudevent-stream/mvnw deleted file mode 100755 index a16b5431b..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/mvnw.cmd b/spring-cloud-function-samples/function-sample-cloudevent-stream/mvnw.cmd deleted file mode 100644 index c8d43372c..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/pom.xml b/spring-cloud-function-samples/function-sample-cloudevent-stream/pom.xml deleted file mode 100644 index 7384a7634..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/pom.xml +++ /dev/null @@ -1,169 +0,0 @@ - - - 4.0.0 - io.spring.sample - function-sample-cloudevent-stream - 0.0.1-SNAPSHOT - function-sample-cloudevent-stream - Demo project for Spring Boot - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.cloud - spring-cloud-function-deployer - - - org.springframework.cloud - spring-cloud-stream - 4.1.0-SNAPSHOT - - - - org.springframework.cloud - spring-cloud-stream-binder-rabbit - 4.1.0-SNAPSHOT - - - - - - org.springframework.cloud - spring-cloud-stream-binder-kafka - 4.1.0-SNAPSHOT - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - maven-surefire-plugin - - - **/*Tests.java - **/*Test.java - - - **/Abstract*.java - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - - diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/DemoApplication.java b/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/DemoApplication.java deleted file mode 100644 index 3e59c45ef..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/DemoApplication.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.spring.cloudevent; - -import java.net.URI; -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.cloudevent.CloudEventHeaderEnricher; -import org.springframework.cloud.function.cloudevent.CloudEventMessageBuilder; -import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -@SpringBootApplication -public class DemoApplication { - - public static void main(String[] args) throws Exception { - SpringApplication.run(DemoApplication.class, args); - } - - @Bean - public Function hire() { - return person -> { - Employee employee = new Employee(person); - return employee; - }; - } - - // uncomment while keeping the above POJO function -// @Bean -// public CloudEventHeaderEnricher cloudEventEnricher() { -// return messageBuilder -> messageBuilder.setSource("http://spring.io/cloudevent") -// .setType("sample").setId("987654"); -// } - - // uncomment while commenting the previous two beans -// @Bean -// public Function, Message> hire() { -// return message -> { -// Person person = message.getPayload(); -// Employee employee = new Employee(person); -// return CloudEventMessageBuilder.withData(employee).setId("123456") -// .setSource(URI.create("https://spring.cloudevenets.sample")).build(); -// }; -// } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/Employee.java b/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/Employee.java deleted file mode 100644 index e1f04615e..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/Employee.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.spring.cloudevent; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Random; - -public class Employee { - - private Person person; - - private int id; - - public Employee() { - - } - - public Employee(Person person) { - this.person = person; - this.id = new Random().nextInt(1000); - } - - public Person getPerson() { - return person; - } - - public void setPerson(Person person) { - this.person = person; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getMessage() { - return "Employee " + id + " was hired on " + new SimpleDateFormat("dd-MM-yyyy").format(new Date()); - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/Person.java b/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/Person.java deleted file mode 100644 index 99ded7514..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/java/io/spring/cloudevent/Person.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.spring.cloudevent; - -public class Person { - - private String firstName; - - private String lastName; - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/resources/application.yaml b/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/resources/application.yaml deleted file mode 100644 index 7dbd85990..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/main/resources/application.yaml +++ /dev/null @@ -1,15 +0,0 @@ -spring: - cloud: - stream: - bindings: - hire-in-0: - binder: rabbit1 - hire-out-0: - binder: kafka1 - binders: - rabbit1: - type: rabbit - kafka1: - type: kafka - function: - definition: hire \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/test/java/io/spring/cloudevent/DemoApplicationTests.java b/spring-cloud-function-samples/function-sample-cloudevent-stream/src/test/java/io/spring/cloudevent/DemoApplicationTests.java deleted file mode 100644 index eb57b7bc6..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/src/test/java/io/spring/cloudevent/DemoApplicationTests.java +++ /dev/null @@ -1,83 +0,0 @@ -package io.spring.cloudevent; - -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.Collections; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.TimeUnit; - -import org.apache.kafka.clients.admin.KafkaAdminClient; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ConditionEvaluationResult; -import org.junit.jupiter.api.extension.ExecutionCondition; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.function.cloudevent.CloudEventMessageBuilder; -import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils; -import org.springframework.kafka.annotation.KafkaListener; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.util.MimeTypeUtils; - -@SpringBootTest -@ExtendWith(DemoApplicationTests.TestRule.class) -public class DemoApplicationTests { - - @Autowired - private RabbitMessagingTemplate rabbitTemplate; - - ArrayBlockingQueue> queue = new ArrayBlockingQueue<>(1); - - @Test - public void test() throws Exception { - Message messageToAMQP = CloudEventMessageBuilder - .withData("{\"firstName\":\"John\", \"lastName\":\"Doe\"}".getBytes()) - .setSource("https://cloudevent.demo") - .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) - .build(CloudEventMessageUtils.AMQP_ATTR_PREFIX); - - rabbitTemplate.send("hire-in-0", "#", messageToAMQP); - - Message resultFromKafka = queue.poll(2000, TimeUnit.MILLISECONDS); - System.out.println("Result Message: " + resultFromKafka); - System.out.println("Cloud Event 'specversion': " + CloudEventMessageUtils.getSpecVersion(resultFromKafka)); - System.out.println("Cloud Event 'source': " + CloudEventMessageUtils.getSource(resultFromKafka)); - System.out.println("Cloud Event 'id': " + CloudEventMessageUtils.getId(resultFromKafka)); - System.out.println("Cloud Event 'type': " + CloudEventMessageUtils.getType(resultFromKafka)); - - } - - @KafkaListener(id = "test", topics = "hire-out-0", clientIdPrefix = "cloudEvents") - public void listen(Message message) { - queue.add(message); - } - - public static class TestRule implements ExecutionCondition { - @Override - public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { - try { - new CachingConnectionFactory("localhost").createConnection(); - try { - Socket socket = new Socket(); - socket.connect(new InetSocketAddress("localhost", 9092)); - socket.close(); - } - catch (Exception e) { - System.out.println("Kafka is not available on localhost:9092"); - return ConditionEvaluationResult.disabled("Kafka is not available on localhost, default port"); - } - } - catch (Exception e) { - System.out.println("RabbitMQ is not available on localhost:5672"); - return ConditionEvaluationResult.disabled("Rabbit is not available on localhost:5672"); - } - - - return ConditionEvaluationResult.enabled("All is good"); - } - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent/.gitignore b/spring-cloud-function-samples/function-sample-cloudevent/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-cloudevent/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud-function-samples/function-sample-cloudevent/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index e76d1f324..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-cloudevent/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55..000000000 Binary files a/spring-cloud-function-samples/function-sample-cloudevent/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-cloudevent/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-cloudevent/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 642d572ce..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-cloud-function-samples/function-sample-cloudevent/README.adoc b/spring-cloud-function-samples/function-sample-cloudevent/README.adoc deleted file mode 100644 index 774439185..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/README.adoc +++ /dev/null @@ -1,47 +0,0 @@ -## Examples of Cloud Events with Spring via HTTP - -### Introduction -The current example uses spring-cloud-function framework as its core which allows users to only worry about functional aspects of -their requirement while taking care-off non-functional aspects. For more information on Spring Cloud Function please visit -our https://spring.io/projects/spring-cloud-function[project page]. - -The example consists of a Spring boot configuration class -https://github.com/spring-cloud/spring-cloud-function/blob/master/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/DemoApplication.java[DemoApplication] -which contains a sample function which you can interact with following via HTTP. - -Given that SCF allows function to be exposed as REST endpoints, you can post cloud event to any of the -functions by using function name as path (e.g., `localhost:8080/`). - - -Here is an example of curl command posting a cloud event in binary-mode: - -[source, text] ----- -curl -w'\n' localhost:8080/hire \ - -H "ce-id: 0001" \ - -H "ce-specversion: 1.0" \ - -H "ce-type: hire" \ - -H "ce-source: spring.io/spring-event" \ - -H "Content-Type: application/json" \ - -d '{"firstName":"John", "lastName":"Doe"}' -i ----- - -And here is an example of curl command posting a cloud event in structured-mode: - -[source, text] ----- -curl -w'\n' localhost:8080/asString \ - -H "Content-Type: application/cloudevents+json" \ - -d '{ - "specversion" : "1.0", - "type" : "org.springframework", - "source" : "https://spring.io/", - "id" : "A234-1234-1234", - "datacontenttype" : "application/json", - "data" : { - "firstName" : "John", - "lastName" : "Doe" - } -}' ----- - diff --git a/spring-cloud-function-samples/function-sample-cloudevent/images/rabbit-send-binary.png b/spring-cloud-function-samples/function-sample-cloudevent/images/rabbit-send-binary.png deleted file mode 100644 index 52bd15117..000000000 Binary files a/spring-cloud-function-samples/function-sample-cloudevent/images/rabbit-send-binary.png and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-cloudevent/images/rabbit-send-structured.png b/spring-cloud-function-samples/function-sample-cloudevent/images/rabbit-send-structured.png deleted file mode 100644 index d5b45d3e7..000000000 Binary files a/spring-cloud-function-samples/function-sample-cloudevent/images/rabbit-send-structured.png and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-cloudevent/mvnw b/spring-cloud-function-samples/function-sample-cloudevent/mvnw deleted file mode 100755 index a16b5431b..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-cloudevent/mvnw.cmd b/spring-cloud-function-samples/function-sample-cloudevent/mvnw.cmd deleted file mode 100644 index c8d43372c..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-cloudevent/pom.xml b/spring-cloud-function-samples/function-sample-cloudevent/pom.xml deleted file mode 100644 index 6b9360838..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/pom.xml +++ /dev/null @@ -1,153 +0,0 @@ - - - 4.0.0 - io.spring.sample - function-sample-cloudevent - 0.0.1-SNAPSHOT - function-sample-cloudevent - Demo project for Spring Boot - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.cloud - spring-cloud-function-web - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - maven-surefire-plugin - - - **/*Tests.java - **/*Test.java - - - **/Abstract*.java - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - - diff --git a/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/DemoApplication.java b/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/DemoApplication.java deleted file mode 100644 index 3e59c45ef..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/DemoApplication.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.spring.cloudevent; - -import java.net.URI; -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.cloudevent.CloudEventHeaderEnricher; -import org.springframework.cloud.function.cloudevent.CloudEventMessageBuilder; -import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -@SpringBootApplication -public class DemoApplication { - - public static void main(String[] args) throws Exception { - SpringApplication.run(DemoApplication.class, args); - } - - @Bean - public Function hire() { - return person -> { - Employee employee = new Employee(person); - return employee; - }; - } - - // uncomment while keeping the above POJO function -// @Bean -// public CloudEventHeaderEnricher cloudEventEnricher() { -// return messageBuilder -> messageBuilder.setSource("http://spring.io/cloudevent") -// .setType("sample").setId("987654"); -// } - - // uncomment while commenting the previous two beans -// @Bean -// public Function, Message> hire() { -// return message -> { -// Person person = message.getPayload(); -// Employee employee = new Employee(person); -// return CloudEventMessageBuilder.withData(employee).setId("123456") -// .setSource(URI.create("https://spring.cloudevenets.sample")).build(); -// }; -// } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/Employee.java b/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/Employee.java deleted file mode 100644 index e1f04615e..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/Employee.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.spring.cloudevent; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Random; - -public class Employee { - - private Person person; - - private int id; - - public Employee() { - - } - - public Employee(Person person) { - this.person = person; - this.id = new Random().nextInt(1000); - } - - public Person getPerson() { - return person; - } - - public void setPerson(Person person) { - this.person = person; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getMessage() { - return "Employee " + id + " was hired on " + new SimpleDateFormat("dd-MM-yyyy").format(new Date()); - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/Person.java b/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/Person.java deleted file mode 100644 index 99ded7514..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/Person.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.spring.cloudevent; - -public class Person { - - private String firstName; - - private String lastName; - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } -} diff --git a/spring-cloud-function-samples/function-sample-cloudevent/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-cloudevent/src/main/resources/application.properties deleted file mode 100644 index 2d0521080..000000000 --- a/spring-cloud-function-samples/function-sample-cloudevent/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -#spring.cloud.function.definition=asPOJOMessage diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/.jdk8 b/spring-cloud-function-samples/function-sample-functional-aws-routing/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/README.adoc b/spring-cloud-function-samples/function-sample-functional-aws-routing/README.adoc deleted file mode 100644 index fc0c41a2c..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/README.adoc +++ /dev/null @@ -1,7 +0,0 @@ -This example demonstrates routing capabilities of spring-cloud-function when deployed as AWS Lambdas. -It is almost identical to the `function-sample-aws-routing` , so most of what's described it its README applies to this example as well. - -The main difference though is that this example uses functional bean definition feature of Spring. - -It also uses `MessageRoutingCallback` in place of `spring_cloud_function_definition` and/or `spring_cloud_function_routingExpression`, effectively giving you more flexibility with your routing decisions. - diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/build.gradle b/spring-cloud-function-samples/function-sample-functional-aws-routing/build.gradle deleted file mode 100644 index a641c315f..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/build.gradle +++ /dev/null @@ -1,93 +0,0 @@ -buildscript { - ext { - springBootVersion = '2.2.0.BUILD-SNAPSHOT' - wrapperVersion = '1.0.17.RELEASE' - shadowVersion = '5.1.0' - } - repositories { - mavenLocal() - jcenter() - mavenCentral() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } - } - dependencies { - classpath "com.github.jengelman.gradle.plugins:shadow:${shadowVersion}" - classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}") - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - classpath("io.spring.gradle:dependency-management-plugin:1.0.8.RELEASE") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'eclipse' -apply plugin: 'com.github.johnrengelman.shadow' -apply plugin: 'org.springframework.boot' -apply plugin: 'org.springframework.boot.experimental.thin-launcher' -apply plugin: 'io.spring.dependency-management' - -group = 'io.spring.sample' -version = '2.0.0.RELEASE' -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -repositories { - mavenLocal() - mavenCentral() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } -} - -ext { - springCloudFunctionVersion = "3.0.0.BUILD-SNAPSHOT" - awsLambdaEventsVersion = "2.0.2" - awsLambdaCoreVersion = "1.1.0" -} -ext['reactor.version'] = "3.1.7.RELEASE" - -assemble.dependsOn = [shadowJar, thinJar] - -jar { - manifest { - attributes 'Main-Class': 'example.Config' - } -} - -import com.github.jengelman.gradle.plugins.shadow.transformers.* - -shadowJar { - classifier = 'aws' - dependencies { - exclude( - dependency("org.springframework.cloud:spring-cloud-function-web:${springCloudFunctionVersion}")) - } - // Required for Spring - mergeServiceFiles() - append 'META-INF/spring.handlers' - append 'META-INF/spring.schemas' - append 'META-INF/spring.tooling' - transform(PropertiesFileTransformer) { - paths = ['META-INF/spring.factories'] - mergeStrategy = "append" - } -} - -configurations { - testCompile.extendsFrom(compileOnly) -} - -dependencyManagement { - imports { - mavenBom "org.springframework.cloud:spring-cloud-function-dependencies:${springCloudFunctionVersion}" - } -} - -dependencies { - compile("org.springframework.cloud:spring-cloud-function-adapter-aws") - compile("org.springframework.cloud:spring-cloud-starter-function-webflux") - compile("org.springframework.boot:spring-boot-configuration-processor") - compileOnly("com.amazonaws:aws-lambda-java-events:${awsLambdaEventsVersion}") - compileOnly("com.amazonaws:aws-lambda-java-core:${awsLambdaCoreVersion}") - testCompile('org.springframework.boot:spring-boot-starter-test') -} diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-function-samples/function-sample-functional-aws-routing/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ca78035ef..000000000 Binary files a/spring-cloud-function-samples/function-sample-functional-aws-routing/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-function-samples/function-sample-functional-aws-routing/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index b33419dee..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/gradlew b/spring-cloud-function-samples/function-sample-functional-aws-routing/gradlew deleted file mode 100755 index 27309d923..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/gradlew.bat b/spring-cloud-function-samples/function-sample-functional-aws-routing/gradlew.bat deleted file mode 100644 index f6d5974e7..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/pom.xml b/spring-cloud-function-samples/function-sample-functional-aws-routing/pom.xml deleted file mode 100644 index ec0799a10..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/pom.xml +++ /dev/null @@ -1,149 +0,0 @@ - - - 4.0.0 - - io.spring.sample - function-sample-functional-aws-routing - 0.0.1-SNAPSHOT - jar - - function-sample-functional-aws-routing - Spring Cloud Function Sample for AWS Lambda - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - UTF-8 - UTF-8 - 1.0.27.RELEASE - 2.0.2 - 4.1.0-SNAPSHOT - - - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - - org.springframework.cloud - spring-cloud-function-web - - - org.springframework.boot - spring-boot-starter-web - - - - com.amazonaws - aws-lambda-java-events - ${aws-lambda-events.version} - - - com.amazonaws - aws-lambda-java-core - 1.1.0 - provided - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - false - true - aws - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/java/example/FunctionConfiguration.java b/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/java/example/FunctionConfiguration.java deleted file mode 100644 index 554133da0..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/java/example/FunctionConfiguration.java +++ /dev/null @@ -1,56 +0,0 @@ -package example; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.MessageRoutingCallback; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.messaging.Message; - -@SpringBootApplication -public class FunctionConfiguration implements ApplicationContextInitializer { - - /* - * You need this main method or explicit example.FunctionConfiguration - * in the POM to ensure boot plug-in makes the correct entry - */ - public static void main(String[] args) { - SpringApplication.run(FunctionConfiguration.class, args); - } - - public Function uppercase() { - return value -> value.toUpperCase(); - } - - public Function reverse() { - return value -> new StringBuilder(value).reverse().toString(); - } - - public static class RoutingCallback implements MessageRoutingCallback { - @Override - public FunctionRoutingResult routingResult(Message message) { - String payload = new String((byte[]) message.getPayload()); - System.out.println("==> Will be routing based on payload: " + payload); - return payload.contains("uppercase") - ? new FunctionRoutingResult("uppercase") - : new FunctionRoutingResult("reverse"); - } - } - - @Override - public void initialize(GenericApplicationContext applicationContext) { - System.out.println("==> Initializing"); - applicationContext.registerBean(MessageRoutingCallback.class, - () -> new RoutingCallback()); - applicationContext.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(uppercase()).type( - FunctionTypeUtils.functionType(String.class, String.class))); - applicationContext.registerBean("reverse", FunctionRegistration.class, - () -> new FunctionRegistration<>(reverse()).type( - FunctionTypeUtils.functionType(String.class, String.class))); - } -} diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/resources/application.properties deleted file mode 100644 index eed9c5e95..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -logging.level.org.springframework.cloud=DEBUG -spring.functional.enabled = false diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/resources/log4j.properties b/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/resources/log4j.properties deleted file mode 100644 index f644d953c..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/resources/log4j.properties +++ /dev/null @@ -1,20 +0,0 @@ -log4j.rootCategory=DEBUG, LAMBDA -PID=???? -LOG_LEVEL_PATTERN=%5p -LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] boot%X{context} - ${PID} ${LOG_LEVEL_PATTERN} [%t] --- %c{1}: %m%n -# CONSOLE is set to be a ConsoleAppender using a PatternLayout. -log4j.appender.LAMBDA=com.amazonaws.services.lambda.runtime.log4j.LambdaAppender -log4j.appender.LAMBDA.layout=org.apache.log4j.PatternLayout -log4j.appender.LAMBDA.layout.conversionPattern=${LOG_PATTERN} -log4j.category.org.apache.catalina.startup.DigesterFactory=ERROR -log4j.category.org.apache.catalina.util.LifecycleBase=ERROR -log4j.category.org.apache.coyote.http11.Http11NioProtocol=WARN -log4j.category.org.apache.sshd.common.util.SecurityUtils -log4j.category.org.apache.tomcat.util.net.NioSelectorPool=WARN -log4j.category.org.crsh.plugin=WARN -log4j.category.org.crsh.ssh=WARN -log4j.category.org.eclipse.jetty.util.component.AbstractLifeCycle=ERROR -log4j.category.org.hibernate.validator.internal.util.Version=WARN -log4j.category.org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration=WARN -log4j.category.org.springframework.boot.actuate.endpoint.jmx=WARN -log4j.category.org.thymeleaf=WARN diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/test/java/example/MapTests.java b/spring-cloud-function-samples/function-sample-functional-aws-routing/src/test/java/example/MapTests.java deleted file mode 100644 index 289a8c9ff..000000000 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/test/java/example/MapTests.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import org.junit.jupiter.api.Test; - -/** - * @author Dave Syer - * - */ -public class MapTests { - - @Test - public void test() { - - } -} diff --git a/spring-cloud-function-samples/function-sample-gcp-background/README.adoc b/spring-cloud-function-samples/function-sample-gcp-background/README.adoc deleted file mode 100644 index c561972a1..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-background/README.adoc +++ /dev/null @@ -1,104 +0,0 @@ -:branch: master - -=== Google Cloud Background Functions Sample Application - -Google Cloud Functions supports deploying https://cloud.google.com/functions/docs/writing/background[Background Functions] which are invoked indirectly in response to an event, such as a message on a https://cloud.google.com/pubsub[Pub/Sub] topic, a change in a https://cloud.google.com/storage[Cloud Storage] bucket, or a https://firebase.google.com/[Firebase] event. - -This sample demonstrates a simple background function which triggers from a Pub/Sub event. - -===== Test locally - -In a terminal window, run: - ----- -mvn function:run ----- - -In a separate window, invoke the background function by issuing the `curl` command below. - -NOTE: In this sample, we demonstrate a background function which is triggered when a message is https://cloud.google.com/functions/docs/calling/pubsub[published to a specified Cloud Pub/Sub topic]. -The `curl` request simulates sending the message that would be received by your function when a Pub/Sub event occurs. - -In order to simulate how Pub/Sub will send you this message, we use a base64-encoded string as the input because Pub/Sub encodes the message data in base64 in the Pub/Sub event. -See the notes section below for a more information on the Pub/Sub event structure. - ----- -curl localhost:8080 -H "Content-Type: application/json" -d '{"data":"aGVsbG8="}' ----- - -In the original terminal window where the `mvn function:run` was invoked, you should see a message printed. - ----- -Received Pub/Sub message with data: hello ----- - -===== Deploy to GCP - -The steps below will demonstrate how to deploy your background function to GCP and have it be invoked by a Pub/Sub event. - -To complete the next steps, make sure that you have the https://cloud.google.com/sdk/install[Cloud SDK CLI] installed. - -1. Create a new Pub/Sub topic. This topic will be used as the trigger for the background function. -+ ----- -gcloud pubsub topics create my-functions-topic ----- - -2. In this sample directory, package the application by running: -+ ----- -mvn package ----- -+ -You should see the JAR to deploy in the `target/deploy` directory. - -3. Deploy the JAR that you created by running -+ ----- -gcloud functions deploy function-sample-gcp-background \ ---entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \ ---runtime java17 \ ---trigger-topic my-functions-topic \ ---source target/deploy \ ---memory 512MB ----- -+ -Notice the parameter `--trigger-topic` which topic will trigger the function invocation when new messages are published to it. - -4. Invoke the background function by publishing a message to your topic. -+ ----- -gcloud pubsub topics publish my-functions-topic --message="Hello world" ----- - -5. To verify that this sample background function was invoked, check the logs of the background function on GCF by running: -+ ----- -gcloud functions logs read function-sample-gcp-background --filter=Received ----- -+ -You should see a log message that looks like below. -It might take a minute or two for the log to appear. -+ ----- -function-sample-gcp-background-2 h8q1jt46069r 2020-05-19 19:48:27.960 Received Pub/Sub message with data: Hello world ----- -+ -This log output is produced by the sample background function, so this entry in the logs means that the background function was successfully invoked. - -===== Notes - -One important note about the Pub/Sub background function provided in `BackgroundFunctionMain` is that the `data` field must be decoded from base64 encoding because Pub/Sub will encode the published message in base64 according to the https://cloud.google.com/functions/docs/calling/pubsub#event_structure[PubSubMessage event structure]. - -[source, java] ----- -@Bean -public Consumer pubSubFunction() { - return message -> { - // The PubSubMessage data field arrives as a base-64 encoded string and must be decoded. - // See: https://cloud.google.com/functions/docs/calling/pubsub#event_structure - String decodedMessage = new String(Base64.decode(message.getData()), StandardCharsets.UTF_8); - System.out.println("Received Pub/Sub message with data: " + decodedMessage); - }; -} ----- diff --git a/spring-cloud-function-samples/function-sample-gcp-background/pom.xml b/spring-cloud-function-samples/function-sample-gcp-background/pom.xml deleted file mode 100644 index 86b3a064b..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-background/pom.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - - 4.0.0 - - io.spring.sample - function-sample-gcp-background - 0.0.1-SNAPSHOT - jar - - function-sample-gcp-background - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - - org.springframework.cloud - spring-cloud-function-adapter-gcp - 4.1.0-SNAPSHOT - - - - - - - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - target/deploy - - - - org.springframework.cloud - spring-cloud-function-adapter-gcp - 3.1.0-SNAPSHOT - - - - - - com.google.cloud.functions - function-maven-plugin - 0.9.1 - - org.springframework.cloud.function.adapter.gcp.GcfJarLauncher - 8080 - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - diff --git a/spring-cloud-function-samples/function-sample-gcp-background/src/main/java/com/example/BackgroundFunctionMain.java b/spring-cloud-function-samples/function-sample-gcp-background/src/main/java/com/example/BackgroundFunctionMain.java deleted file mode 100644 index 753fe6322..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-background/src/main/java/com/example/BackgroundFunctionMain.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.example; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.function.Consumer; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class BackgroundFunctionMain { - - public static void main(String[] args) { - SpringApplication.run(BackgroundFunctionMain.class, args); - } - - /** - * The background function which triggers on an event from Pub/Sub and consumes the Pub/Sub - * event message. - */ - @Bean - public Consumer pubSubFunction() { - return message -> { - // The PubSubMessage data field arrives as a base-64 encoded string and must be decoded. - // See: https://cloud.google.com/functions/docs/calling/pubsub#event_structure - String decodedMessage = new String(Base64.getDecoder().decode(message.getData()), StandardCharsets.UTF_8); - System.out.println("Received Pub/Sub message with data: " + decodedMessage); - }; - } -} diff --git a/spring-cloud-function-samples/function-sample-gcp-background/src/main/java/com/example/PubSubMessage.java b/spring-cloud-function-samples/function-sample-gcp-background/src/main/java/com/example/PubSubMessage.java deleted file mode 100644 index 01b5084fa..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-background/src/main/java/com/example/PubSubMessage.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.example; - -import java.util.Map; - -/** - * A class that can be mapped to the GCF Pub/Sub Message event type. This is for use in - * the background functions. - * - *

See the PubSubMessage definition for reference: - * https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage - * - * @author Mike Eltsufin - */ -public class PubSubMessage { - - private String data; - - private Map attributes; - - private String messageId; - - private String publishTime; - - public String getData() { - return data; - } - - public void setData(String data) { - this.data = data; - } - - public Map getAttributes() { - return attributes; - } - - public void setAttributes(Map attributes) { - this.attributes = attributes; - } - - public String getMessageId() { - return messageId; - } - - public void setMessageId(String messageId) { - this.messageId = messageId; - } - - public String getPublishTime() { - return publishTime; - } - - public void setPublishTime(String publishTime) { - this.publishTime = publishTime; - } - -} diff --git a/spring-cloud-function-samples/function-sample-gcp-background/src/main/resources/META-INF/MANIFEST.MF b/spring-cloud-function-samples/function-sample-gcp-background/src/main/resources/META-INF/MANIFEST.MF deleted file mode 100644 index edd9a3bf2..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-background/src/main/resources/META-INF/MANIFEST.MF +++ /dev/null @@ -1 +0,0 @@ -Main-Class: com.example.BackgroundFunctionMain diff --git a/spring-cloud-function-samples/function-sample-gcp-http/README.adoc b/spring-cloud-function-samples/function-sample-gcp-http/README.adoc deleted file mode 100644 index 43a1c422c..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-http/README.adoc +++ /dev/null @@ -1,46 +0,0 @@ -:branch: master - -=== Google Cloud Functions Sample Application - -===== Test locally - -Run the function: - ----- -mvn function:run ----- - -Invoke the HTTP function: - ----- -curl http://localhost:8080/ -d "hello" ----- - -===== Deploy to GCP - -Package the application. - ----- -mvn package ----- - -You should see the fat jar in the `target/deploy` directory. - -Make sure that you have the https://cloud.google.com/sdk/install[Cloud SDK CLI] installed. - -Run the following command from the project root to deploy. - ----- -gcloud functions deploy function-sample-gcp-http \ ---entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \ ---runtime java17 \ ---trigger-http \ ---source target/deploy \ ---memory 512MB ----- - -Invoke the HTTP function: - ----- -curl https://REGION-PROJECT_ID.cloudfunctions.net/function-sample-gcp-http -d "hello" ----- diff --git a/spring-cloud-function-samples/function-sample-gcp-http/pom.xml b/spring-cloud-function-samples/function-sample-gcp-http/pom.xml deleted file mode 100644 index b5da383c9..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-http/pom.xml +++ /dev/null @@ -1,156 +0,0 @@ - - - - 4.0.0 - - io.spring.sample - function-sample-gcp-http - 0.0.1-SNAPSHOT - jar - - function-sample-gcp-http - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 4.1.0-SNAPSHOT - - - - - org.springframework.cloud - spring-cloud-function-adapter-gcp - 4.1.0-SNAPSHOT - - - - - org.springframework - spring-web - test - - - org.springframework.boot - spring-boot-test - test - - - org.junit.jupiter - junit-jupiter - test - - - org.assertj - assertj-core - test - - - com.google.cloud.functions.invoker - java-function-invoker - 1.0.0-alpha-2-rc5 - test - - - - - - - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - target/deploy - - - - org.springframework.cloud - spring-cloud-function-adapter-gcp - ${spring-cloud-function.version} - - - - - - com.google.cloud.functions - function-maven-plugin - 0.9.1 - - org.springframework.cloud.function.adapter.gcp.GcfJarLauncher - 8080 - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample-gcp-http/src/main/java/com/example/CloudFunctionMain.java b/spring-cloud-function-samples/function-sample-gcp-http/src/main/java/com/example/CloudFunctionMain.java deleted file mode 100644 index dc5f213ab..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-http/src/main/java/com/example/CloudFunctionMain.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -@SpringBootApplication -public class CloudFunctionMain { - - public static void main(String[] args) { - SpringApplication.run(CloudFunctionMain.class, args); - } - - @Bean - public Function function() { - return value -> value.toUpperCase(); - } -} diff --git a/spring-cloud-function-samples/function-sample-gcp-http/src/main/resources/META-INF/MANIFEST.MF b/spring-cloud-function-samples/function-sample-gcp-http/src/main/resources/META-INF/MANIFEST.MF deleted file mode 100644 index 6bd1d52fe..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-http/src/main/resources/META-INF/MANIFEST.MF +++ /dev/null @@ -1 +0,0 @@ -Main-Class: com.example.CloudFunctionMain diff --git a/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java b/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java deleted file mode 100644 index 2ebd4f819..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.io.IOException; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.test.web.client.TestRestTemplate; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FunctionSampleGcpIntegrationTest { - - private TestRestTemplate rest = new TestRestTemplate(); - - @Test - public void testSample() throws IOException, InterruptedException { - try (LocalServerTestSupport.ServerProcess process = LocalServerTestSupport.startServer(CloudFunctionMain.class)) { - String result = rest.postForObject("http://localhost:8080/", "Hello", String.class); - assertThat(result).isEqualTo("\"HELLO\""); - } - } -} diff --git a/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/LocalServerTestSupport.java b/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/LocalServerTestSupport.java deleted file mode 100644 index 8bbe71943..000000000 --- a/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/LocalServerTestSupport.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.io.*; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import com.google.cloud.functions.invoker.runner.Invoker; - -import org.springframework.cloud.function.adapter.gcp.GcfJarLauncher; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Test support class for running tests on the local Cloud Function server. - * - * @author Daniel Zou - * @author Mike Eltsufin - */ -final public class LocalServerTestSupport { - - private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(); - - private static final String SERVER_READY_STRING = "Started ServerConnector"; - - private static AtomicInteger nextPort = new AtomicInteger(8080); - - private LocalServerTestSupport() { - } - - public static ServerProcess startServer(Class springApplicationMainClass) - throws InterruptedException, IOException { - - // Get the Java class path. - String myClassPath = System.getProperty("java.class.path"); - assertThat(myClassPath).isNotNull(); - - // Setup the Java Process command line string - List command = Arrays.asList(getJavaCommand(), "-classpath", myClassPath, Invoker.class.getName()); - ProcessBuilder processBuilder = new ProcessBuilder().command(command).redirectErrorStream(true); - - // Set environment variables. - Map environment = new HashMap<>(); - environment.put("PORT", String.valueOf(nextPort.getAndIncrement())); - environment.put("K_SERVICE", "test-function"); - environment.put("FUNCTION_SIGNATURE_TYPE", "http"); - environment.put("FUNCTION_TARGET", GcfJarLauncher.class.getCanonicalName()); - environment.put("MAIN_CLASS", springApplicationMainClass.getCanonicalName()); - processBuilder.environment().putAll(environment); - - // Start the process and monitor the output logs in a separate thread. - // Once the SERVER_READY_STRING is found in the logs, we know we are ready. - Process serverProcess = processBuilder.start(); - CountDownLatch ready = new CountDownLatch(1); - - EXECUTOR.submit(() -> monitorOutput(serverProcess.getInputStream(), ready)); - boolean serverReady = ready.await(5, TimeUnit.SECONDS); - if (!serverReady) { - serverProcess.destroy(); - throw new AssertionError("Server never became ready"); - } - - return new ServerProcess(serverProcess); - } - - private static void monitorOutput(InputStream processOutput, CountDownLatch ready) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(processOutput))) { - String line; - while ((line = reader.readLine()) != null) { - if (line.contains(SERVER_READY_STRING)) { - ready.countDown(); - } - - System.out.println(line); - - if (line.contains("WARNING")) { - throw new AssertionError("Found warning in server output:\n" + line); - } - } - } - catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - /** - * Returns the path to the java executable. - */ - private static String getJavaCommand() { - File javaHome = new File(System.getProperty("java.home")); - assertThat(javaHome.exists()).isTrue(); - - File javaBin = new File(javaHome, "bin"); - File javaCommand = new File(javaBin, "java"); - assertThat(javaCommand.exists()).isTrue(); - - return javaCommand.toString(); - } - - static class ServerProcess implements AutoCloseable { - - private final Process process; - - ServerProcess(Process process) { - this.process = process; - } - - Process process() { - return process; - } - - @Override - public void close() { - process().destroy(); - } - } -} diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/.gitignore b/spring-cloud-function-samples/function-sample-grpc-cloudevent/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud-function-samples/function-sample-grpc-cloudevent/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index e76d1f324..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-grpc-cloudevent/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55..000000000 Binary files a/spring-cloud-function-samples/function-sample-grpc-cloudevent/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-grpc-cloudevent/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index abd303b67..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/mvnw b/spring-cloud-function-samples/function-sample-grpc-cloudevent/mvnw deleted file mode 100755 index a16b5431b..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/mvnw.cmd b/spring-cloud-function-samples/function-sample-grpc-cloudevent/mvnw.cmd deleted file mode 100644 index c8d43372c..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/pom.xml b/spring-cloud-function-samples/function-sample-grpc-cloudevent/pom.xml deleted file mode 100644 index 720ee2e24..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/pom.xml +++ /dev/null @@ -1,148 +0,0 @@ - - - 4.0.0 - - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - com.example.grpc - function-sample-grpc-cloudevent - 0.0.1-SNAPSHOT - function-sample-grpc-cloudevent - Demo project for Spring Boot - - 4.1.0-SNAPSHOT - 1.55.1 - - - - org.springframework.boot - spring-boot-starter - - - org.springframework.cloud - spring-cloud-function-grpc-cloudevent-ext - - - - io.grpc - grpc-netty - ${grpc.version} - - - io.grpc - grpc-protobuf - ${grpc.version} - - - io.grpc - grpc-stub - ${grpc.version} - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - kr.motd.maven - os-maven-plugin - 1.6.1 - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - 0.6.1 - - - com.google.protobuf:protoc:3.23.0:exe:${os.detected.classifier} - - grpc-java - - io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} - - - - - - compile - compile-custom - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - false - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/java/com/example/grpc/demo/DemoGrpcApplication.java b/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/java/com/example/grpc/demo/DemoGrpcApplication.java deleted file mode 100644 index 83d7bcc33..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/java/com/example/grpc/demo/DemoGrpcApplication.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.example.grpc.demo; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.function.grpc.MessagingServiceGrpc; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -import com.google.protobuf.DescriptorProtos.FileDescriptorProto; -import com.google.protobuf.DescriptorProtos.FileDescriptorSet; -import com.google.protobuf.Descriptors.FileDescriptor; -import com.google.protobuf.ProtocolStringList; - -import io.cloudevents.v1.CloudEventServiceGrpc; -import io.cloudevents.v1.proto.CloudEvent; -import io.cloudevents.v1.proto.CloudEvent.CloudEventAttributeValue; -import io.grpc.ManagedChannel; -import io.grpc.ManagedChannelBuilder; - -@SpringBootApplication -public class DemoGrpcApplication { - - public static void main(String[] args) throws Exception { - - SpringApplication.run(DemoGrpcApplication.class, - "--spring.cloud.function.grpc.service-class-name=org.springframework.cloud.function.grpc.ce.CloudEventHandler"); - - CloudEvent cloudEvent = CloudEvent.newBuilder() - .setTextData("{\"event_name\":\"SCF supports CloudEvent gRPC\"}") - .setSource("http://springsource.com") - .setId("12345") - .setSpecVersion("1.0") - .setType("org.springframework") - .putAttributes("name", CloudEventAttributeValue.newBuilder().setCeString("oleg").build()) - .putAttributes("fluent_in_french", CloudEventAttributeValue.newBuilder().setCeBoolean(false).build()) - .build(); - - ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 6048) - .usePlaintext().build(); - - CloudEventServiceGrpc.CloudEventServiceBlockingStub stub = CloudEventServiceGrpc.newBlockingStub(channel); - CloudEvent reply = stub.requestReply(cloudEvent); - System.out.println(reply); - - } - - @Bean - public Function, Message> uppercase() { - return message -> { - return MessageBuilder.withPayload(message.getPayload().toUpperCase()) - .copyHeaders(message.getHeaders()) - .setHeader("uppercased", "true") - .build(); - }; - } -} - - diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/proto/CloudEvent.proto b/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/proto/CloudEvent.proto deleted file mode 100644 index 7952c1f79..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/proto/CloudEvent.proto +++ /dev/null @@ -1,49 +0,0 @@ -syntax = "proto3"; - -package io.cloudevents.v1; - -import "google/protobuf/any.proto"; -import "google/protobuf/timestamp.proto"; - -option go_package = "cloudevents.io/genproto/v1"; -option java_package = "io.cloudevents.v1.proto"; -option java_multiple_files = true; - -message CloudEvent { - - // -- CloudEvent Context Attributes - - // Required Attributes - string id = 1; - string source = 2; // URI-reference - string spec_version = 3; - string type = 4; - - // Optional & Extension Attributes - map attributes = 5; - - // -- CloudEvent Data (Bytes, Text, or Proto) - oneof data { - bytes binary_data = 6; - string text_data = 7; - google.protobuf.Any proto_data = 8; - } - - /** - * The CloudEvent specification defines - * seven attribute value types... - */ - - message CloudEventAttributeValue { - - oneof attr { - bool ce_boolean = 1; - int32 ce_integer = 2; - string ce_string = 3; - bytes ce_bytes = 4; - string ce_uri = 5; - string ce_uri_ref = 6; - google.protobuf.Timestamp ce_timestamp = 7; - } - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/proto/CloudEventService.proto b/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/proto/CloudEventService.proto deleted file mode 100644 index 1a7be6a74..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/proto/CloudEventService.proto +++ /dev/null @@ -1,17 +0,0 @@ -syntax = "proto3"; - -package io.cloudevents.v1; - -import "google/protobuf/any.proto"; -import "google/protobuf/timestamp.proto"; -import "CloudEvent.proto"; - -service CloudEventService { - rpc biStream(stream io.cloudevents.v1.CloudEvent) returns (stream io.cloudevents.v1.CloudEvent); - - rpc clientStream(stream io.cloudevents.v1.CloudEvent) returns (io.cloudevents.v1.CloudEvent); - - rpc serverStream(io.cloudevents.v1.CloudEvent) returns (stream io.cloudevents.v1.CloudEvent); - - rpc requestReply(io.cloudevents.v1.CloudEvent) returns (io.cloudevents.v1.CloudEvent); -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/resources/application.properties deleted file mode 100644 index 8b1378917..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/test/java/com/example/grpc/demo/DemoGrpcApplicationTests.java b/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/test/java/com/example/grpc/demo/DemoGrpcApplicationTests.java deleted file mode 100644 index 452a7eed5..000000000 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/test/java/com/example/grpc/demo/DemoGrpcApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.grpc.demo; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class DemoGrpcApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/.gitignore b/spring-cloud-function-samples/function-sample-kotlin-web/.gitignore deleted file mode 100644 index 549e00a2a..000000000 --- a/spring-cloud-function-samples/function-sample-kotlin-web/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud-function-samples/function-sample-kotlin-web/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index e76d1f324..000000000 --- a/spring-cloud-function-samples/function-sample-kotlin-web/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-function-samples/function-sample-kotlin-web/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55..000000000 Binary files a/spring-cloud-function-samples/function-sample-kotlin-web/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-function-samples/function-sample-kotlin-web/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 642d572ce..000000000 --- a/spring-cloud-function-samples/function-sample-kotlin-web/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/README.adoc b/spring-cloud-function-samples/function-sample-kotlin-web/README.adoc deleted file mode 100644 index bceb0ad5e..000000000 --- a/spring-cloud-function-samples/function-sample-kotlin-web/README.adoc +++ /dev/null @@ -1,45 +0,0 @@ -## Examples of Kotlin support in Spring Cloud Function - -### Introduction -This example provides a configuration with a single Kotlin function - -[source, kotlin] ----- -@Configuration -class DemoKotlinConfiguration { - @Bean - fun uppercase(): (String) -> String { - return { it.toUpperCase() } - } -} ----- - -It also adds web support - -[source, xml] ----- - - org.springframework.cloud - spring-cloud-function-kotlin - ${spring-cloud-function.version} - - - org.springframework.cloud - spring-cloud-function-web - ${spring-cloud-function.version} - ----- - -Once you start the application, simply issue GET request either thru browser or curl: - -[source, text] ----- -curl http://localhost:8080/uppercase/hello ----- - -you should see the output - -[source, text] ----- -HELLO ----- diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/images/rabbit-send-binary.png b/spring-cloud-function-samples/function-sample-kotlin-web/images/rabbit-send-binary.png deleted file mode 100644 index 52bd15117..000000000 Binary files a/spring-cloud-function-samples/function-sample-kotlin-web/images/rabbit-send-binary.png and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/images/rabbit-send-structured.png b/spring-cloud-function-samples/function-sample-kotlin-web/images/rabbit-send-structured.png deleted file mode 100644 index d5b45d3e7..000000000 Binary files a/spring-cloud-function-samples/function-sample-kotlin-web/images/rabbit-send-structured.png and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/mvnw b/spring-cloud-function-samples/function-sample-kotlin-web/mvnw deleted file mode 100755 index a16b5431b..000000000 --- a/spring-cloud-function-samples/function-sample-kotlin-web/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/mvnw.cmd b/spring-cloud-function-samples/function-sample-kotlin-web/mvnw.cmd deleted file mode 100644 index c8d43372c..000000000 --- a/spring-cloud-function-samples/function-sample-kotlin-web/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/pom.xml b/spring-cloud-function-samples/function-sample-kotlin-web/pom.xml deleted file mode 100644 index c067fc3c3..000000000 --- a/spring-cloud-function-samples/function-sample-kotlin-web/pom.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - 4.0.0 - io.spring.sample - function-sample-kotlin-web - 0.0.1-SNAPSHOT - function-sample-kotlin-web - Demo project for Spring Cloud Function Web Kotlin integration - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - - org.springframework.boot - spring-boot-starter - - - org.jetbrains.kotlin - kotlin-reflect - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - - - org.springframework.cloud - spring-cloud-function-kotlin - 4.1.0-SNAPSHOT - - - org.springframework.cloud - spring-cloud-function-web - 4.1.0-SNAPSHOT - - - org.springframework.cloud - spring-cloud-function-context - 4.1.0-SNAPSHOT - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - ${project.basedir}/src/main/kotlin - ${project.basedir}/src/test/kotlin - - - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.jetbrains.kotlin - kotlin-maven-plugin - - - -Xjsr305=strict - - - spring - - - - - org.jetbrains.kotlin - kotlin-maven-allopen - ${kotlin.version} - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - - diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/src/main/kotlin/com/example/kotlin/DemoKotlinApplication.kt b/spring-cloud-function-samples/function-sample-kotlin-web/src/main/kotlin/com/example/kotlin/DemoKotlinApplication.kt deleted file mode 100644 index 126ba69f0..000000000 --- a/spring-cloud-function-samples/function-sample-kotlin-web/src/main/kotlin/com/example/kotlin/DemoKotlinApplication.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.example.kotlin - -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.runApplication -import org.springframework.context.annotation.Bean -import java.util.function.Function -import org.springframework.cloud.function.context.FunctionCatalog -import kotlin.jvm.internal.Reflection -import kotlin.jvm.javaClass - -@SpringBootApplication -open class DemoKotlinApplication - -fun main(args: Array) { - runApplication(*args) -} diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/src/main/kotlin/com/example/kotlin/DemoKotlinConfiguration.kt b/spring-cloud-function-samples/function-sample-kotlin-web/src/main/kotlin/com/example/kotlin/DemoKotlinConfiguration.kt deleted file mode 100644 index ca207e7fd..000000000 --- a/spring-cloud-function-samples/function-sample-kotlin-web/src/main/kotlin/com/example/kotlin/DemoKotlinConfiguration.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.example.kotlin - -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.runApplication -import org.springframework.context.annotation.Bean -import java.util.function.Function -import org.springframework.context.annotation.Configuration - -@Configuration -class DemoKotlinConfiguration { - @Bean - fun uppercase(): (String) -> String { - return { it.toUpperCase() } - } -} - - - diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-kotlin-web/src/main/resources/application.properties deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-samples/function-sample-pof/build.gradle b/spring-cloud-function-samples/function-sample-pof/build.gradle deleted file mode 100644 index d62d81852..000000000 --- a/spring-cloud-function-samples/function-sample-pof/build.gradle +++ /dev/null @@ -1,50 +0,0 @@ -buildscript { - ext { - springBootVersion = '1.5.12.RELEASE' - wrapperVersion = '1.0.11.RELEASE' - } - repositories { - mavenLocal() - mavenCentral() - maven { url "https://repo.spring.io/plugins-snapshot" } - maven { url "https://repo.spring.io/plugins-milestone" } - } - dependencies { - classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}") - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'eclipse' -apply plugin: 'spring-boot' -apply plugin: 'org.springframework.boot.experimental.thin-launcher' - -group = 'io.spring.sample' -version = '2.0.0.RELEASE' -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -repositories { - mavenLocal() - mavenCentral() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } -} - -ext { - springCloudFunctionVersion = "2.0.0.BUILD-SNAPSHOT" -} -ext['reactor.version'] = "3.1.7.RELEASE" - -dependencyManagement { - imports { - mavenBom "org.springframework.cloud:spring-cloud-function-dependencies:${springCloudFunctionVersion}" - } -} - -dependencies { - compile('org.springframework.cloud:spring-cloud-starter-function-web') - testCompile('org.springframework.boot:spring-boot-starter-test') -} diff --git a/spring-cloud-function-samples/function-sample-pof/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-function-samples/function-sample-pof/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ca78035ef..000000000 Binary files a/spring-cloud-function-samples/function-sample-pof/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-pof/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-function-samples/function-sample-pof/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index a0b285197..000000000 --- a/spring-cloud-function-samples/function-sample-pof/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip diff --git a/spring-cloud-function-samples/function-sample-pof/gradlew b/spring-cloud-function-samples/function-sample-pof/gradlew deleted file mode 100755 index 27309d923..000000000 --- a/spring-cloud-function-samples/function-sample-pof/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/spring-cloud-function-samples/function-sample-pof/gradlew.bat b/spring-cloud-function-samples/function-sample-pof/gradlew.bat deleted file mode 100644 index f6d5974e7..000000000 --- a/spring-cloud-function-samples/function-sample-pof/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/spring-cloud-function-samples/function-sample-pof/pom.xml b/spring-cloud-function-samples/function-sample-pof/pom.xml deleted file mode 100644 index 6ac64b4bd..000000000 --- a/spring-cloud-function-samples/function-sample-pof/pom.xml +++ /dev/null @@ -1,131 +0,0 @@ - - 4.0.0 - - io.spring.sample - function-sample-pof - 0.0.1-SNAPSHOT - jar - function-sample-pof - Spring Cloud Function Web Support - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - UTF-8 - UTF-8 - 3.1.2.RELEASE - 4.1.0-SNAPSHOT - - - - - org.springframework.cloud - spring-cloud-starter-function-web - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - exec - - - - org.springframework.boot.experimental - spring-boot-thin-layout - 1.0.27.RELEASE - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample-pof/src/main/java/functions/Application.java b/spring-cloud-function-samples/function-sample-pof/src/main/java/functions/Application.java deleted file mode 100644 index f645883c5..000000000 --- a/spring-cloud-function-samples/function-sample-pof/src/main/java/functions/Application.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package functions; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -// @checkstyle:off -@SpringBootApplication -public class Application { - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } - -} -// @checkstyle:on diff --git a/spring-cloud-function-samples/function-sample-pof/src/main/java/functions/Greeter.java b/spring-cloud-function-samples/function-sample-pof/src/main/java/functions/Greeter.java deleted file mode 100644 index 2f1eed816..000000000 --- a/spring-cloud-function-samples/function-sample-pof/src/main/java/functions/Greeter.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package functions; - -import java.util.function.Function; - -// @checkstyle:off -public class Greeter implements Function { - - public String apply(String name) { - return "Hello " + name; - } - -} -// @checkstyle:on diff --git a/spring-cloud-function-samples/function-sample-pojo/.jdk8 b/spring-cloud-function-samples/function-sample-pojo/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-samples/function-sample-pojo/build.gradle b/spring-cloud-function-samples/function-sample-pojo/build.gradle deleted file mode 100644 index d033c4a27..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/build.gradle +++ /dev/null @@ -1,50 +0,0 @@ -buildscript { - ext { - springBootVersion = '2.0.3.RELEASE' - wrapperVersion = '1.0.13.RELEASE' - } - repositories { - mavenLocal() - mavenCentral() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } - } - dependencies { - classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}") - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'eclipse' -apply plugin: 'spring-boot' -apply plugin: 'org.springframework.boot.experimental.thin-launcher' - -group = 'io.spring.sample' -version = '2.0.0.RELEASE' -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -repositories { - mavenLocal() - mavenCentral() - maven { url "https://repo.spring.io/plugins-snapshot" } - maven { url "https://repo.spring.io/plugins-milestone" } -} - -ext { - springCloudFunctionVersion = "2.0.0.BUILD-SNAPSHOT" -} -ext['reactor.version'] = "3.1.7.RELEASE" - -dependencyManagement { - imports { - mavenBom "org.springframework.cloud:spring-cloud-function-dependencies:${springCloudFunctionVersion}" - } -} - -dependencies { - compile('org.springframework.cloud:spring-cloud-starter-function-web') - testCompile('org.springframework.boot:spring-boot-starter-test') -} diff --git a/spring-cloud-function-samples/function-sample-pojo/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-function-samples/function-sample-pojo/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ca78035ef..000000000 Binary files a/spring-cloud-function-samples/function-sample-pojo/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-pojo/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-function-samples/function-sample-pojo/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index a0b285197..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip diff --git a/spring-cloud-function-samples/function-sample-pojo/gradlew b/spring-cloud-function-samples/function-sample-pojo/gradlew deleted file mode 100755 index 27309d923..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/spring-cloud-function-samples/function-sample-pojo/gradlew.bat b/spring-cloud-function-samples/function-sample-pojo/gradlew.bat deleted file mode 100644 index f6d5974e7..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/spring-cloud-function-samples/function-sample-pojo/pom.xml b/spring-cloud-function-samples/function-sample-pojo/pom.xml deleted file mode 100644 index aca6a77fc..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/pom.xml +++ /dev/null @@ -1,150 +0,0 @@ - - - 4.0.0 - - io.spring.sample - function-sample-pojo - 0.0.1-SNAPSHOT - jar - function-sample-pojo - Spring Cloud Function Web Support - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.cloud - spring-cloud-starter-function-webflux - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - maven-surefire-plugin - - - **/*Tests.java - **/*Test.java - - - **/Abstract*.java - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/LowercaseConfiguration.java b/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/LowercaseConfiguration.java deleted file mode 100644 index 45afa2e5c..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/LowercaseConfiguration.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.util.function.Function; - -import reactor.core.publisher.Flux; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @author Dave Syer - * - */ -@Configuration(proxyBeanMethods = false) -public class LowercaseConfiguration { - - @Bean - public Function, Flux> lowercase() { - return flux -> flux.log().map(value -> new Bar(value.lowercase())); - } - -} diff --git a/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java b/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java deleted file mode 100644 index 52fe3dbf5..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Supplier; - -import reactor.core.publisher.Flux; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.util.MultiValueMap; - -// @checkstyle:off -@SpringBootApplication(proxyBeanMethods = false) -public class SampleApplication { - - public static void main(String[] args) { - SpringApplication.run(SampleApplication.class, args); - } - - @Bean - public Function uppercase() { - return value -> new Bar(value.uppercase()); - } - - @Bean - public Function, Map> sum() { - return multiValueMap -> { - Map result = new HashMap<>(); - multiValueMap.forEach((s, strings) -> result.put(s, - strings.stream().mapToInt(Integer::parseInt).sum())); - return result; - }; - } - - @Bean - public Supplier> words() { - return () -> Flux.fromArray(new Foo[] {new Foo("foo"), new Foo("bar")}).log(); - } - -} -// @checkstyle:on - -class Foo { - - private String value; - - Foo() { - } - - Foo(String value) { - this.value = value; - } - - public String lowercase() { - return this.value.toLowerCase(); - } - - public String uppercase() { - return this.value.toUpperCase(); - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - -} - -class Bar { - - private String value; - - Bar() { - } - - Bar(String value) { - this.value = value; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - -} diff --git a/spring-cloud-function-samples/function-sample-pojo/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-pojo/src/main/resources/application.properties deleted file mode 100644 index b5dc321cf..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/src/main/resources/application.properties +++ /dev/null @@ -1,4 +0,0 @@ -spring.cloud.stream.bindings.input.destination:foos -spring.cloud.stream.bindings.output.destination:bars -spring.cloud.function.stream.processor.name:uppercase -management.security.enabled:false diff --git a/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationMvcTests.java b/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationMvcTests.java deleted file mode 100644 index 15c366478..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationMvcTests.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.net.URI; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -public class SampleApplicationMvcTests { - - @Autowired - private TestRestTemplate rest; - - @Test - public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")) - .accept(MediaType.APPLICATION_JSON).build(), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); - } - -} diff --git a/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationTests.java b/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationTests.java deleted file mode 100644 index c70be8011..000000000 --- a/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationTests.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.net.URI; -import java.util.Arrays; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.util.LinkedMultiValueMap; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -public class SampleApplicationTests { - - private HttpHeaders headers; - - @LocalServerPort - private int port; - - private TestRestTemplate rest = new TestRestTemplate(); - - @BeforeEach - public void before() { - this.headers = new HttpHeaders(); - this.headers.setContentType(MediaType.APPLICATION_JSON); - } - - @Test - public void words() { - assertThat(this.rest - .getForObject("http://localhost:" + this.port + "/words", String.class)) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); - } - - @Test - public void uppercase() { - assertThat(this.rest.postForObject("http://localhost:" + this.port + "/uppercase", - new HttpEntity<>("[{\"value\":\"foo\"}]", this.headers), String.class)) - .isEqualTo("[{\"value\":\"FOO\"}]"); - } - - @Test - public void composite() { - assertThat(this.rest - .getForObject("http://localhost:" + this.port + "/words,uppercase", - String.class)).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - public void single() { - assertThat(this.rest.postForObject("http://localhost:" + this.port + "/uppercase", - new HttpEntity<>("{\"value\":\"foo\"}", this.headers), String.class)) - .isEqualTo("{\"value\":\"FOO\"}"); - } - - @Test - public void lowercase() { - assertThat(this.rest.postForObject("http://localhost:" + this.port + "/lowercase", - new HttpEntity<>("[{\"value\":\"Foo\"}]", this.headers), String.class)) - .isEqualTo("[{\"value\":\"foo\"}]"); - } - - @Test - public void sum() throws Exception { - - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - - map.put("A", Arrays.asList("1", "2", "3")); - map.put("B", Arrays.asList("5", "6")); - - assertThat(this.rest.exchange( - RequestEntity.post(new URI("http://localhost:" + this.port + "/sum")) - .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_FORM_URLENCODED).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); - } - - @Test - public void multipart() throws Exception { - - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - - map.put("A", Arrays.asList("1", "2", "3")); - map.put("B", Arrays.asList("5", "6")); - - assertThat(this.rest.exchange( - RequestEntity.post(new URI("http://localhost:" + this.port + "/sum")) - .accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); - } - -} diff --git a/spring-cloud-function-samples/function-sample-spring-integration/.gitignore b/spring-cloud-function-samples/function-sample-spring-integration/.gitignore deleted file mode 100644 index 9243c63d7..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -.gradle -/build/ -!gradle/wrapper/gradle-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr -/out/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-spring-integration/build.gradle b/spring-cloud-function-samples/function-sample-spring-integration/build.gradle deleted file mode 100644 index 2138d4667..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/build.gradle +++ /dev/null @@ -1,43 +0,0 @@ -buildscript { - ext { - springBootVersion = '2.1.2.RELEASE' - } - repositories { - mavenCentral() - maven { url 'https://repo.spring.io/libs-milestone' } - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' - -group = 'io.spring.sample' -version = '0.0.1-SNAPSHOT' -sourceCompatibility = 1.8 - -repositories { - mavenCentral() - maven { url "https://repo.spring.io/milestone" } -} - - -ext { - springCloudFunctionVersion = '2.1.0.BUILD-SNAPSHOT' -} - -dependencies { - implementation('org.springframework.boot:spring-boot-starter-integration') - implementation('org.springframework.cloud:spring-cloud-starter-function-webflux') - testImplementation('org.springframework.boot:spring-boot-starter-test') -} - -dependencyManagement { - imports { - mavenBom "org.springframework.cloud:spring-cloud-function-dependencies:${springCloudFunctionVersion}" - } -} diff --git a/spring-cloud-function-samples/function-sample-spring-integration/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-function-samples/function-sample-spring-integration/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 1ce6e58f1..000000000 Binary files a/spring-cloud-function-samples/function-sample-spring-integration/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample-spring-integration/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-function-samples/function-sample-spring-integration/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 448cc64e5..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Tue Feb 06 12:27:20 CET 2018 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip diff --git a/spring-cloud-function-samples/function-sample-spring-integration/gradlew b/spring-cloud-function-samples/function-sample-spring-integration/gradlew deleted file mode 100644 index 4453ccea3..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/gradlew +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env sh - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save ( ) { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/spring-cloud-function-samples/function-sample-spring-integration/gradlew.bat b/spring-cloud-function-samples/function-sample-spring-integration/gradlew.bat deleted file mode 100644 index f9553162f..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/spring-cloud-function-samples/function-sample-spring-integration/pom.xml b/spring-cloud-function-samples/function-sample-spring-integration/pom.xml deleted file mode 100644 index 40dd8b3d0..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/pom.xml +++ /dev/null @@ -1,140 +0,0 @@ - - 4.0.0 - - io.spring.sample - function-sample-spring-integration - 0.0.1-SNAPSHOT - jar - function-sample-spring-integration - Spring Cloud Function Web Support - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - UTF-8 - UTF-8 - 17 - 4.1.0-SNAPSHOT - - - - - org.springframework.cloud - spring-cloud-starter-function-webflux - - - org.springframework.boot - spring-boot-starter-integration - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - exec - - - - org.springframework.boot.experimental - spring-boot-thin-layout - 1.0.27.RELEASE - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample-spring-integration/settings.gradle b/spring-cloud-function-samples/function-sample-spring-integration/settings.gradle deleted file mode 100644 index 55d13ed71..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'function-sample-spring-integration' diff --git a/spring-cloud-function-samples/function-sample-spring-integration/src/main/java/example/FunctionSampleSpringIntegrationApplication.java b/spring-cloud-function-samples/function-sample-spring-integration/src/main/java/example/FunctionSampleSpringIntegrationApplication.java deleted file mode 100644 index f6103c277..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/src/main/java/example/FunctionSampleSpringIntegrationApplication.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import java.util.function.Function; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.integration.dsl.IntegrationFlow; -import org.springframework.integration.handler.LoggingHandler; -import org.springframework.messaging.Message; - -@SpringBootApplication -public class FunctionSampleSpringIntegrationApplication { - - public static void main(String[] args) { - SpringApplication.run(FunctionSampleSpringIntegrationApplication.class, args); - } - - @Bean - public IntegrationFlow uppercaseFlow() { - return IntegrationFlow.from(MessageFunction.class, (gateway) -> gateway.beanName("uppercase")) - .transform(String::toUpperCase) - .log(LoggingHandler.Level.WARN) - .get(); - } - - public interface MessageFunction extends Function, Message> { - - } - -} diff --git a/spring-cloud-function-samples/function-sample-spring-integration/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-spring-integration/src/main/resources/application.properties deleted file mode 100644 index 8b1378917..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring-cloud-function-samples/function-sample-spring-integration/src/test/java/example/FunctionSampleSpringIntegrationApplicationTests.java b/spring-cloud-function-samples/function-sample-spring-integration/src/test/java/example/FunctionSampleSpringIntegrationApplicationTests.java deleted file mode 100644 index b6ee09a11..000000000 --- a/spring-cloud-function-samples/function-sample-spring-integration/src/test/java/example/FunctionSampleSpringIntegrationApplicationTests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2019-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package example; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class FunctionSampleSpringIntegrationApplicationTests { - - @Autowired - private TestRestTemplate restTemplate; - - @Test - public void upperCase() { - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentType(MediaType.APPLICATION_JSON); - HttpEntity requestEntity = new HttpEntity<>("[\"foo\", \"bar\"]", httpHeaders); - HttpEntity result = this.restTemplate.postForEntity("/uppercase", requestEntity, String.class); - assertThat(result.getBody()).isEqualTo("[\"FOO\",\"BAR\"]"); - } - -} diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/README.md b/spring-cloud-function-samples/function-sample-supplier-exporter/README.md deleted file mode 100644 index 560073481..000000000 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/README.md +++ /dev/null @@ -1,16 +0,0 @@ -AWS Lambda custom runtime. - -``` -$ ./build.sh -$ ./mvnw package -P native -``` - -builds a native-zip ZIP file in target. Upload it to AWS and set the handler to "foobar". - -To test locally, run the `TestServer` and then the `DemoApplication` (either in a JVM or natively). Then POST some data into the test server: - -``` -$ curl localhost:8000/add -d world -H "Content-Type: text/plain" -``` - -There is a unit test that does the same thing. Also the `build.sh` script orchestrates the same test for the native image. diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/compile.sh b/spring-cloud-function-samples/function-sample-supplier-exporter/compile.sh deleted file mode 100644 index ec45e4d25..000000000 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/compile.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash - -ARTIFACT=function-aws -MAINCLASS=com.example.demo.DemoApplication -VERSION=0.0.1-SNAPSHOT -FEATURE=../../../../spring-graal-native/target/spring-graal-native-0.7.0.BUILD-SNAPSHOT.jar - -GREEN='\033[0;32m' -RED='\033[0;31m' -NC='\033[0m' - -rm -rf target -mkdir -p target/native-image - -echo "Packaging $ARTIFACT with Maven" -mvn -DskipTests package > target/native-image/output.txt - -JAR="$ARTIFACT-$VERSION.jar" -rm -f $ARTIFACT -echo "Unpacking $JAR" -cd target/native-image -jar -xvf ../$JAR >/dev/null 2>&1 -cp -R META-INF BOOT-INF/classes - -LIBPATH=`find BOOT-INF/lib | tr '\n' ':'` -CP=BOOT-INF/classes:$LIBPATH:$FEATURE - -if [ ! -f "$FEATURE" ]; then - printf "${RED}FAILURE${NC}: $FEATURE does not exist, please build the root project before building this sample.\n" - exit 1 -fi - -GRAALVM_VERSION=`native-image --version` -echo "Compiling $ARTIFACT with $GRAALVM_VERSION" -{ time native-image \ - --verbose \ - --no-server \ - --no-fallback \ - --initialize-at-build-time \ - -H:+PrintMethodHistogram \ - -H:+TraceClassInitialization \ - -H:Name=$ARTIFACT \ - -H:+ReportExceptionStackTraces \ - -Dspring.graal.remove-unused-autoconfig=true \ - -Dspring.graal.remove-yaml-support=true \ - -cp $CP $MAINCLASS >> output.txt ; } 2>> output.txt - -if [[ -f $ARTIFACT ]] -then - printf "${GREEN}SUCCESS${NC}\n" - mv ./$ARTIFACT .. - exit 0 -else - cat output.txt - printf "${RED}FAILURE${NC}: an error occurred when compiling the native-image.\n" - exit 1 -fi - diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/pom.xml b/spring-cloud-function-samples/function-sample-supplier-exporter/pom.xml deleted file mode 100644 index be230cf80..000000000 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/pom.xml +++ /dev/null @@ -1,155 +0,0 @@ - - - 4.0.0 - - io.spring.sample - function-sample-aws-supplier-exporter - 0.0.1-SNAPSHOT - jar - function-sample-aws-supplier-exporter - Spring Cloud Function Example showing Supplier Exporter - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 4.1.0-SNAPSHOT - - - - - org.springframework.boot - spring-boot-starter-webflux - - - org.springframework.cloud - spring-cloud-function-web - - - org.springframework.cloud - spring-cloud-function-adapter-aws - - - org.springframework.boot - spring-boot-starter-test - test - - - org.awaitility - awaitility - test - - - org.testcontainers - testcontainers - 1.14.3 - test - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - maven-surefire-plugin - - - com/example/demo/ContainerTests.java - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - - integration - - - - maven-surefire-plugin - 2.22.0 - - - integration-test - - test - - - - none - - - com/example/demo/ContainerTests.java - - - - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - - diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/java/com/example/demo/DemoApplication.java b/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/java/com/example/demo/DemoApplication.java deleted file mode 100644 index fd7b20ea4..000000000 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/java/com/example/demo/DemoApplication.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.example.demo; - -import java.util.function.Function; - -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionalSpringApplication; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.support.GenericApplicationContext; - -@SpringBootConfiguration(proxyBeanMethods = false) -public class DemoApplication - implements ApplicationContextInitializer { - - public static void main(String[] args) { - FunctionalSpringApplication.run(DemoApplication.class, args); - } - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("foobar", FunctionRegistration.class, - () -> new FunctionRegistration<>(new Foobar()) - .type(FunctionTypeUtils.functionType(Foo.class, Foo.class))); - } - -} - -class Foo { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Foo(String name) { - this.name = name; - } - - Foo() { - } -} - -class Foobar implements Function { - - @Override - public Foo apply(Foo input) { - System.err.println("HI: " + input.getName()); - return new Foo("hi " + input.getName() + "!"); - } -} \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/resources/META-INF/native-image/reflect-config.json b/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/resources/META-INF/native-image/reflect-config.json deleted file mode 100644 index c5f6007b7..000000000 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/resources/META-INF/native-image/reflect-config.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "name": "com.example.demo.Foo", - "allDeclaredConstructors": true, - "allDeclaredMethods": true - } -] \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/resources/application.properties deleted file mode 100644 index 9e8695a2c..000000000 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/resources/application.properties +++ /dev/null @@ -1,4 +0,0 @@ -spring.cloud.function.web.export.enabled=true -spring.cloud.function.web.export.debug=true -spring.main.web-application-type=none -logging.level.org.springframework.cloud=DEBUG \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/src/test/java/com/example/demo/ContainerTests.java b/spring-cloud-function-samples/function-sample-supplier-exporter/src/test/java/com/example/demo/ContainerTests.java deleted file mode 100644 index 0b16a54a5..000000000 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/src/test/java/com/example/demo/ContainerTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2020-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.example.demo; - -import java.util.concurrent.TimeUnit; - -import org.awaitility.Awaitility; -import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.ToStringConsumer; -import org.testcontainers.utility.MountableFile; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -public class ContainerTests { - - @Test - void test() throws Exception { - ToStringConsumer consumer = new ToStringConsumer(); - try (@SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("lambci/lambda:provided").withLogConsumer(consumer) - .withCopyFileToContainer(MountableFile.forClasspathResource("bootstrap"), "/var/task/") - .withEnv("DOCKER_LAMBDA_STAY_OPEN", "1").withExposedPorts(9001)) { - container.start(); - int port = container.getFirstMappedPort(); - String host = container.getHost(); - System.err.println(host + ":" + port); - DemoApplication.main(new String[] { "--AWS_LAMBDA_RUNTIME_API=" + host + ":" + port, "--_HANDLER=foobar", - "--logging.level.org.springframework=DEBUG" }); - ResponseEntity response = Awaitility.waitAtMost(30, TimeUnit.SECONDS).until(() -> { - ResponseEntity result = new RestTemplate().postForEntity( - "http://" + host + ":" + port + "/2015-03-31/functions/foobar/invocations", - "{\"name\":\"foo\"}", String.class); - return result; - }, result -> result != null); - assertThat(response.getBody()).contains("hi foo!"); - assertThat(response.getHeaders()).containsKey("X-Amzn-Requestid"); - } - String output = consumer.toUtf8String(); - assertThat(output).contains("Lambda API listening on port 9001"); - assertThat(output).contains("START RequestId:"); - assertThat(output).contains("END RequestId:"); - } - -} diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/src/test/resources/bootstrap b/spring-cloud-function-samples/function-sample-supplier-exporter/src/test/resources/bootstrap deleted file mode 100755 index 142d4caad..000000000 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/src/test/resources/bootstrap +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -while true -do - sleep 1 -done \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample/.jdk8 b/spring-cloud-function-samples/function-sample/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-samples/function-sample/build.gradle b/spring-cloud-function-samples/function-sample/build.gradle deleted file mode 100644 index f2976cab9..000000000 --- a/spring-cloud-function-samples/function-sample/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -buildscript { - ext { - springBootVersion = '2.1.0.BUILD-SNAPSHOT' - wrapperVersion = '1.0.13.RELEASE' - } - repositories { - mavenLocal() - mavenCentral() - maven { url "https://repo.spring.io/plugins-snapshot" } - maven { url "https://repo.spring.io/plugins-milestone" } - } - dependencies { - classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}") - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - } -} - -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'eclipse' -apply plugin: 'spring-boot' -apply plugin: 'org.springframework.boot.experimental.thin-launcher' - -group = 'com.example' -version = '0.0.1-SNAPSHOT' -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -repositories { - mavenLocal() - mavenCentral() - maven { url "https://repo.spring.io/snapshot" } - maven { url "https://repo.spring.io/milestone" } -} - -ext { - springCloudFunctionVersion = "2.0.0.BUILD-SNAPSHOT" -} - -dependencyManagement { - imports { - mavenBom "org.springframework.cloud:spring-cloud-function-dependencies:${springCloudFunctionVersion}" - } -} - -dependencies { - compile('org.springframework.cloud:spring-cloud-starter-function-web') - testCompile('org.springframework.boot:spring-boot-starter-test') -} diff --git a/spring-cloud-function-samples/function-sample/gradle/wrapper/gradle-wrapper.jar b/spring-cloud-function-samples/function-sample/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ca78035ef..000000000 Binary files a/spring-cloud-function-samples/function-sample/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/spring-cloud-function-samples/function-sample/gradle/wrapper/gradle-wrapper.properties b/spring-cloud-function-samples/function-sample/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index a0b285197..000000000 --- a/spring-cloud-function-samples/function-sample/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip diff --git a/spring-cloud-function-samples/function-sample/gradlew b/spring-cloud-function-samples/function-sample/gradlew deleted file mode 100755 index 27309d923..000000000 --- a/spring-cloud-function-samples/function-sample/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/spring-cloud-function-samples/function-sample/gradlew.bat b/spring-cloud-function-samples/function-sample/gradlew.bat deleted file mode 100644 index f6d5974e7..000000000 --- a/spring-cloud-function-samples/function-sample/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/spring-cloud-function-samples/function-sample/pom.xml b/spring-cloud-function-samples/function-sample/pom.xml deleted file mode 100644 index 5a81da08b..000000000 --- a/spring-cloud-function-samples/function-sample/pom.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - 4.0.0 - - io.spring.sample - function-sample - 0.0.1-SNAPSHOT - jar - function-sample - Spring Cloud Function Web Support - - - org.springframework.boot - spring-boot-starter-parent - 3.2.0-SNAPSHOT - - - - - 4.1.0-SNAPSHOT - 1.0.27.RELEASE - - - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.cloud - spring-cloud-starter-function-webflux - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function.version} - pom - import - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - true - - - false - - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - https://repo.spring.io/libs-release-local - - false - - - - - diff --git a/spring-cloud-function-samples/function-sample/src/main/java/com/example/Client.java b/spring-cloud-function-samples/function-sample/src/main/java/com/example/Client.java deleted file mode 100644 index 516fa3a1c..000000000 --- a/spring-cloud-function-samples/function-sample/src/main/java/com/example/Client.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import org.springframework.web.reactive.function.client.WebClient; - -/** - * Sample client to test infinite stream from function. - * - * @author Oleg Zhurakousky - * - */ -public class Client { - - public static void main(String[] args) throws Exception { - WebClient client = WebClient.create(); - WebClient.ResponseSpec responseSpec = client.post() - .uri("http://localhost:8080/infinite") - .header("accept", "text/event-stream") - .retrieve(); - - responseSpec.bodyToFlux(String.class).subscribe(v -> { - System.out.println(v); - }); - - System.in.read(); - - } - -} diff --git a/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java b/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java deleted file mode 100644 index f26aec07f..000000000 --- a/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.time.Duration; -import java.util.function.Function; -import java.util.function.Supplier; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.messaging.Message; - -// @checkstyle:off -@SpringBootApplication -public class SampleApplication { - - public static void main(String[] args) throws Exception { - SpringApplication.run(SampleApplication.class, "--management.endpoints.web.exposure.include=functions"); - } - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } - - @Bean - public Function, Integer> uppercaseMessage() { - return value -> value.getPayload().toUpperCase().length(); - } - - @Bean - public Function, Flux> lowercase() { - return flux -> flux.map(value -> value.toLowerCase()); - } - - @Bean - public Supplier hello() { - return () -> "hello"; - } - - @Bean - public Supplier> infinite() { - return () -> Flux - .interval(Duration.ofSeconds(1)) - .log() - .map(counter -> String.format("Counter: %s", counter)); - } - -} diff --git a/spring-cloud-function-samples/function-sample/src/main/java/com/example/functions/CharCounter.java b/spring-cloud-function-samples/function-sample/src/main/java/com/example/functions/CharCounter.java deleted file mode 100644 index 5c54b7371..000000000 --- a/spring-cloud-function-samples/function-sample/src/main/java/com/example/functions/CharCounter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.functions; - -import java.util.function.Function; - -/** - * @author Mark Fisher - */ -public class CharCounter implements Function { - - @Override - public Integer apply(String word) { - return word.length(); - } - -} diff --git a/spring-cloud-function-samples/function-sample/src/main/java/com/example/functions/Exclaimer.java b/spring-cloud-function-samples/function-sample/src/main/java/com/example/functions/Exclaimer.java deleted file mode 100644 index 434a34e2d..000000000 --- a/spring-cloud-function-samples/function-sample/src/main/java/com/example/functions/Exclaimer.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.functions; - -import java.util.function.Function; - -import reactor.core.publisher.Flux; - -/** - * @author Mark Fisher - */ -public class Exclaimer implements Function, Flux> { - - @Override - public Flux apply(Flux words) { - return words.map(word -> word + "!!!"); - } - -} diff --git a/spring-cloud-function-samples/function-sample/src/main/java/com/example/functions/Greeter.java b/spring-cloud-function-samples/function-sample/src/main/java/com/example/functions/Greeter.java deleted file mode 100644 index fdf66f032..000000000 --- a/spring-cloud-function-samples/function-sample/src/main/java/com/example/functions/Greeter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.functions; - -import java.util.function.Function; - -/** - * @author Mark Fisher - */ -public class Greeter implements Function { - - @Override - public String apply(String name) { - return "Hello " + name; - } - -} diff --git a/spring-cloud-function-samples/function-sample/src/main/resources/META-INF/thin-rabbit.properties b/spring-cloud-function-samples/function-sample/src/main/resources/META-INF/thin-rabbit.properties deleted file mode 100644 index 6906ff808..000000000 --- a/spring-cloud-function-samples/function-sample/src/main/resources/META-INF/thin-rabbit.properties +++ /dev/null @@ -1,5 +0,0 @@ -boms.spring-cloud-dependencies:org.springframework.cloud:spring-cloud-dependencies:Edgware.SR3 -dependencies.spring-cloud-function-stream:org.springframework.cloud:spring-cloud-function-stream -dependencies.spring-cloud-stream-rabbit:org.springframework.cloud:spring-cloud-starter-stream-rabbit -exclusions.spring-cloud-function-web:org.springframework.cloud:spring-cloud-starter-function-web -exclusions.http-client:com.rabbitmq:http-client diff --git a/spring-cloud-function-samples/function-sample/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample/src/main/resources/application.properties deleted file mode 100644 index c1a56fa72..000000000 --- a/spring-cloud-function-samples/function-sample/src/main/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -spring.cloud.function.stream.processor.name:uppercase -spring.cloud.function.scan.packages:com.example.functions diff --git a/spring-cloud-function-samples/function-sample/src/test/java/com/example/FunctionTests.java b/spring-cloud-function-samples/function-sample/src/test/java/com/example/FunctionTests.java deleted file mode 100644 index cae8e9187..000000000 --- a/spring-cloud-function-samples/function-sample/src/test/java/com/example/FunctionTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.util.List; - -import com.example.functions.CharCounter; -import com.example.functions.Exclaimer; -import com.example.functions.Greeter; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import static org.assertj.core.api.Assertions.assertThat; - -public class FunctionTests { - - private final SampleApplication functions = new SampleApplication(); - - @Test - public void testUppercase() { - String output = this.functions.uppercase().apply("foobar"); - assertThat(output).isEqualTo("FOOBAR"); - } - - @Test - public void testLowercase() { - Flux output = this.functions.lowercase().apply(Flux.just("FOO", "BAR")); - List results = output.collectList().block(); - assertThat(results.size()).isEqualTo(2); - assertThat(results.get(0)).isEqualTo("foo"); - assertThat(results.get(1)).isEqualTo("bar"); - } - - @Test - public void testHello() { - String output = this.functions.hello().get(); - assertThat(output).isEqualTo("hello"); - } - - @Test - public void testGreeter() { - assertThat(new Greeter().apply("World")).isEqualTo("Hello World"); - } - - @Test - public void testExclaimer() { - Flux input = Flux.just("foo", "bar"); - Flux output = new Exclaimer().apply(input); - List results = output.collectList().block(); - assertThat(results.size()).isEqualTo(2); - assertThat(results.get(0)).isEqualTo("foo!!!"); - assertThat(results.get(1)).isEqualTo("bar!!!"); - } - - @Test - public void testCharCounter() { - assertThat(new CharCounter().apply("this is 21 chars long")) - .isEqualTo((Integer) 21); - } - -} diff --git a/spring-cloud-function-samples/function-sample/src/test/java/com/example/SampleApplicationMvcTests.java b/spring-cloud-function-samples/function-sample/src/test/java/com/example/SampleApplicationMvcTests.java deleted file mode 100644 index b9eb7a8cb..000000000 --- a/spring-cloud-function-samples/function-sample/src/test/java/com/example/SampleApplicationMvcTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; - -/** - * @author Dave Syer - */ -@SpringBootTest -@AutoConfigureMockMvc -@Disabled -public class SampleApplicationMvcTests { - - @Autowired - private MockMvc mockMvc; - - @Test - public void words() throws Exception { - MvcResult result = this.mockMvc.perform(get("/words")).andReturn(); - mockMvc.perform(asyncDispatch(result)).andExpect(content().string("[\"foo\",\"bar\"]")); - } - - @Test - public void uppercase() throws Exception { - MvcResult result = this.mockMvc.perform(post("/uppercase").contentType(MediaType.TEXT_PLAIN).content("foo")).andReturn(); - mockMvc.perform(asyncDispatch(result)).andExpect(content().string("FOO")); - } - - @Test - public void lowercase() throws Exception { - MvcResult result = this.mockMvc.perform(post("/lowercase").contentType(MediaType.TEXT_PLAIN).content("FOO")).andReturn(); - mockMvc.perform(asyncDispatch(result)).andExpect(content().string("[\"foo\"]")); - } - - @Test - public void lowercaseMulti() throws Exception { - MvcResult result = this.mockMvc.perform(post("/lowercase").contentType(MediaType.APPLICATION_JSON).content("[\"FOO\"]")).andReturn(); - mockMvc.perform(asyncDispatch(result)).andExpect(content().string("[\"foo\"]")); - } - -} diff --git a/spring-cloud-function-samples/function-sample/src/test/java/com/example/WebTestClientTests.java b/spring-cloud-function-samples/function-sample/src/test/java/com/example/WebTestClientTests.java deleted file mode 100644 index 9e4cfc703..000000000 --- a/spring-cloud-function-samples/function-sample/src/test/java/com/example/WebTestClientTests.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.example; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.test.web.reactive.server.WebTestClient; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.ArrayList; -import java.util.List; - -@FunctionalSpringBootTest -@AutoConfigureWebTestClient -public class WebTestClientTests { - - @Autowired - private WebTestClient client; - - @Test - public void uppercase() { - client.post().uri("/uppercase").body(Mono.just("foo"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("FOO"); - } - - @Test - public void lowercase() { - client.post().uri("/lowercase").body(Flux.just("FOO", "BAR"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("[\"foobar\"]"); - } - - @Test - public void lowercaseMulti() { - client.post().uri("/lowercase").contentType(MediaType.APPLICATION_JSON).body(Mono.just("[\"FOO\"]"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("[\"foo\"]"); - } - - @Test - public void testCollection() { - client.post().uri("/lowercase").contentType(MediaType.APPLICATION_JSON).body(Mono.just("[\"FOO\", \"BAR\"]"), String.class) - .exchange().expectBody(String.class).isEqualTo("[\"foo\",\"bar\"]"); - } - -} diff --git a/spring-cloud-function-samples/pom.xml b/spring-cloud-function-samples/pom.xml deleted file mode 100644 index 6a9b3cff4..000000000 --- a/spring-cloud-function-samples/pom.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-samples - Spring Cloud Function Samples - pom - - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - - - function-sample - function-sample-pof - function-sample-pojo - function-sample-aws - function-functional-sample-aws - function-sample-aws-routing - function-sample-aws-custom - function-sample-aws-custom-bean - function-sample-supplier-exporter - function-sample-azure - function-sample-spring-integration - function-sample-gcp-http - function-sample-gcp-background - function-sample-cloudevent - function-sample-cloudevent-stream - function-sample-cloudevent-rsocket - function-sample-cloudevent-sdk - function-sample-kotlin-web - function-sample-grpc-cloudevent - - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.8.2 - - true - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/libs-snapshot-local - - - spring-milestones - Spring Milestones - https://repo.spring.io/libs-milestone-local - - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - diff --git a/spring-cloud-function-web/.jdk8 b/spring-cloud-function-web/.jdk8 deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-cloud-function-web/pom.xml b/spring-cloud-function-web/pom.xml deleted file mode 100644 index f070fd462..000000000 --- a/spring-cloud-function-web/pom.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - 4.0.0 - - spring-cloud-function-web - jar - Spring Cloud Function Web Support - Spring Cloud Function Web Support - - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - - - - jakarta.servlet - jakarta.servlet-api - provided - - - org.springframework - spring-webmvc - true - - - org.springframework - spring-webflux - true - - - io.projectreactor.netty - reactor-netty - true - - - org.springframework.cloud - spring-cloud-function-context - - - org.springframework.boot - spring-boot-configuration-processor - true - - - commons-logging - commons-logging - 1.2 - provided - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-web - test - - - org.awaitility - awaitility - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.springframework.boot.experimental - spring-boot-thin-layout - ${wrapper.version} - - - - - - - diff --git a/spring-cloud-function-web/refactoring_notes b/spring-cloud-function-web/refactoring_notes deleted file mode 100644 index d4b44cad3..000000000 --- a/spring-cloud-function-web/refactoring_notes +++ /dev/null @@ -1,6 +0,0 @@ -If a function returns Flux, we must represent output as JSON Array/Collection since we never know how many elements such flux will contain per each invocation. -For that same reason we can't use TEXT/PLAIN as CT - -NON-WEB -When sendng collection of objects to function who's input is not collection, the inpt will be converted to flux and the result is alos going to be flux. -That is to ensure that the function is invoked with idividual. . . \ No newline at end of file diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/BasicStringConverter.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/BasicStringConverter.java deleted file mode 100644 index e3634fd1c..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/BasicStringConverter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web; - -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.support.DefaultConversionService; - -/** - * Simple implementation of a {@link StringConverter}. - * - * @author Dave Syer - */ -public class BasicStringConverter implements StringConverter { - - private ConversionService conversionService; - - private ConfigurableListableBeanFactory registry; - - public BasicStringConverter(ConfigurableListableBeanFactory registry) { - this.registry = registry; - } - - @Override - public Object convert(Object function, String value) { - if (this.conversionService == null && this.registry != null) { - ConversionService conversionService = this.registry.getConversionService(); - this.conversionService = conversionService != null ? conversionService - : new DefaultConversionService(); - } - //Class type = this.inspector.getInputType(function); - Class type = function == null ? Object.class : FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(((FunctionInvocationWrapper) function).getInputType())); - return this.conversionService.canConvert(String.class, type) - ? this.conversionService.convert(value, type) : value; - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionHttpProperties.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionHttpProperties.java deleted file mode 100644 index c6b9bd87a..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/FunctionHttpProperties.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.function.context.FunctionProperties; - -/** -* -* @author Oleg Zhurakousky -* @since 4.0.4 -* -*/ -@ConfigurationProperties(prefix = FunctionProperties.PREFIX + ".http") -public class FunctionHttpProperties { - - /** - * Function definition mappings for GET method (e.g. 'spring.cloud.function.http.GET=foo;bar|baz') - */ - public String get; - - - /** - * Function definition mappings for POST method (e.g. 'spring.cloud.function.http.POST=foo;bar|baz') - */ - public String post; - - /** - * Function definition mappings for PUT method (e.g. 'spring.cloud.function.http.PUT=foo;bar|baz') - */ - public String put; - - /** - * Function definition mappings for DELETE method (e.g. 'spring.cloud.function.http.DELETE=foo;bar|baz') - */ - public String delete; - - public String getGet() { - return this.get; - } - - public void setGet(String get) { - this.get = get; - } - - public String getPost() { - return post; - } - - public void setPost(String post) { - this.post = post; - } - - public String getPut() { - return put; - } - - public void setPut(String put) { - this.put = put; - } - - public String getDelete() { - return delete; - } - - public void setDelete(String delete) { - this.delete = delete; - } -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RestApplication.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RestApplication.java deleted file mode 100644 index 529aded42..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RestApplication.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; - -/** - * @author Mark Fisher - */ -// @checkstyle:off -@SpringBootConfiguration(proxyBeanMethods = false) -@EnableAutoConfiguration -public class RestApplication { - - public static void main(String[] args) { - SpringApplication.run(RestApplication.class, args); - } - -} -// @checkstyle:on diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/StringConverter.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/StringConverter.java deleted file mode 100644 index 904476142..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/StringConverter.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web; - -/** - * @author Dave Syer - * - */ -public interface StringConverter { - - Object convert(Object function, String value); - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java deleted file mode 100644 index 2a73afac5..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/constants/WebRequestConstants.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.constants; - -/** - * Common storage for web request attribute names (in a separate package to avoid cycles). - * - * @author Dave Syer - */ -public abstract class WebRequestConstants { - - /** - * Function attribute name. - */ - public static final String FUNCTION = WebRequestConstants.class.getName() - + ".function"; - - /** - * Consumer attribute name. - */ - public static final String CONSUMER = WebRequestConstants.class.getName() - + ".consumer"; - - /** - * Supplier attribute name. - */ - public static final String SUPPLIER = WebRequestConstants.class.getName() - + ".supplier"; - - /** - * Argument attribute name. - */ - public static final String ARGUMENT = WebRequestConstants.class.getName() - + ".argument"; - - /** - * Handler attribute name. - */ - public static final String HANDLER = WebRequestConstants.class.getName() + ".handler"; - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java deleted file mode 100644 index 239ac546e..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionController.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2016-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.flux; - -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.web.FunctionHttpProperties; -import org.springframework.cloud.function.web.constants.WebRequestConstants; -import org.springframework.cloud.function.web.util.FunctionWebRequestProcessingHelper; -import org.springframework.cloud.function.web.util.FunctionWrapper; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.http.codec.multipart.FormFieldPart; -import org.springframework.http.codec.multipart.Part; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.server.ServerWebExchange; - -/** - * @author Dave Syer - * @author Mark Fisher - * @author Oleg Zhurakousky - */ -@Component -@EnableConfigurationProperties(FunctionHttpProperties.class) -public class FunctionController { - - private final FunctionHttpProperties functionHttpProperties; - - public FunctionController(FunctionHttpProperties functionHttpProperties) { - this.functionHttpProperties = functionHttpProperties; - } - - @SuppressWarnings("unchecked") - @PostMapping(path = "/**", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) - @ResponseBody - public Mono> form(ServerWebExchange request) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return request.getFormData().doOnSuccess(params -> wrapper.getParams().addAll(params)) - .then(Mono.defer(() -> (Mono>) FunctionWebRequestProcessingHelper - .processRequest(wrapper, wrapper.getParams(), false))); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); - } - - } - - @SuppressWarnings("unchecked") - @PostMapping(path = "/**", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - @ResponseBody - public Mono> multipart(ServerWebExchange request) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return request.getMultipartData() - .doOnSuccess(params -> wrapper.getParams().addAll(multi(params))) - .then(Mono.defer(() -> (Mono>) FunctionWebRequestProcessingHelper - .processRequest(wrapper, wrapper.getParams(), false))); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); - } - } - - @SuppressWarnings("unchecked") - @PostMapping(path = "/**") - @ResponseBody - public Mono> post(ServerWebExchange request, - @RequestBody(required = false) String body) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); - } - } - - @SuppressWarnings("unchecked") - @PutMapping(path = "/**") - @ResponseBody - public Mono> put(ServerWebExchange request, - @RequestBody(required = false) String body) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("PUT", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("PUT", wrapper.getFunction().getFunctionDefinition())); - } - } - - @SuppressWarnings("unchecked") - @DeleteMapping(path = "/**") - @ResponseBody - public Mono> delete(ServerWebExchange request, - @RequestBody(required = false) String body) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("DELETE", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("DELETE", wrapper.getFunction().getFunctionDefinition())); - } - } - - @PostMapping(path = "/**", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - @ResponseBody - public Publisher postStream(ServerWebExchange request, @RequestBody(required = false) Flux body) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, true); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); - } - - } - - @GetMapping(path = "/**", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - @ResponseBody - public Publisher getStream(ServerWebExchange request) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), true); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition())); - } - } - - @SuppressWarnings("unchecked") - @GetMapping(path = "/**") - @ResponseBody - public Mono> get(ServerWebExchange request) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition())); - } - } - - private FunctionWrapper wrapper(ServerWebExchange request) { - FunctionInvocationWrapper function = (FunctionInvocationWrapper) request - .getAttribute(WebRequestConstants.HANDLER); - HttpHeaders headers = HttpHeaders.writableHttpHeaders(request.getRequest().getHeaders()); - headers.set("uri", request.getRequest().getURI().toString()); - FunctionWrapper wrapper = new FunctionWrapper(function); - wrapper.setHeaders(headers); - wrapper.getParams().addAll(request.getRequest().getQueryParams()); - String argument = (String) request.getAttribute(WebRequestConstants.ARGUMENT); - if (argument != null) { - wrapper.setArgument(argument); - } - return wrapper; - } - - private MultiValueMap multi(MultiValueMap body) { - MultiValueMap map = new LinkedMultiValueMap<>(); - for (String key : body.keySet()) { - for (Part part : body.get(key)) { - if (part instanceof FormFieldPart) { - FormFieldPart form = (FormFieldPart) part; - map.add(key, form.value()); - } - } - } - return map; - } -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java deleted file mode 100644 index 579bf3aa1..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/FunctionHandlerMapping.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.flux; - - -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.web.constants.WebRequestConstants; -import org.springframework.cloud.function.web.util.FunctionWebRequestProcessingHelper; -import org.springframework.context.annotation.Configuration; -import org.springframework.util.StringUtils; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; -import org.springframework.web.server.ServerWebExchange; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -@Configuration -@ConditionalOnClass(RequestMappingHandlerMapping.class) -public class FunctionHandlerMapping extends RequestMappingHandlerMapping - implements InitializingBean { - - private final FunctionCatalog functions; - - private final FunctionController controller; - - private final FunctionProperties functionProperties; - - @Value("${spring.cloud.function.web.path:}") - private String prefix = ""; - - @Autowired - public FunctionHandlerMapping(FunctionCatalog catalog, - FunctionController controller, FunctionProperties functionProperties) { - this.functions = catalog; - this.logger.info("FunctionCatalog: " + catalog); - setOrder(super.getOrder() - 5); - this.controller = controller; - this.functionProperties = functionProperties; - } - - @Override - public void afterPropertiesSet() { - super.afterPropertiesSet(); - detectHandlerMethods(this.controller); - while (this.prefix.endsWith("/")) { - this.prefix = this.prefix.substring(0, this.prefix.length() - 1); - } - } - - @Override - public Mono getHandlerInternal(ServerWebExchange request) { - String path = request.getRequest().getPath().pathWithinApplication().value(); - if (StringUtils.hasText(this.prefix) && !path.startsWith(this.prefix)) { - return Mono.empty(); - } - Mono handler = super.getHandlerInternal(request); - if (path == null) { - return handler; - } - if (path.startsWith(this.prefix)) { - path = path.substring(this.prefix.length()); - } - Object function = FunctionWebRequestProcessingHelper - .findFunction(this.functionProperties, request.getRequest().getMethod(), this.functions, request.getAttributes(), path); - - if (function != null) { - if (this.logger.isDebugEnabled()) { - this.logger.debug("Found function for POST: " + path); - } - request.getAttributes().put(WebRequestConstants.HANDLER, function); - } - Object actual = function; - return handler.filter(method -> actual != null); - } - - @Override - protected void initHandlerMethods() { - } -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java deleted file mode 100644 index 0aade98bb..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/flux/ReactorAutoConfiguration.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.flux; - -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; -import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; -import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.web.BasicStringConverter; -import org.springframework.cloud.function.web.StringConverter; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; - -/** - * @author Dave Syer - * @author Mark Fisher - * @author Oleg Zhurakousky - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass({ Flux.class, AsyncHandlerMethodReturnValueHandler.class }) -@ConditionalOnWebApplication(type = Type.REACTIVE) -@Import(FunctionController.class) -@AutoConfigureAfter({ JacksonAutoConfiguration.class, GsonAutoConfiguration.class }) -public class ReactorAutoConfiguration { - - @Bean - public FunctionHandlerMapping functionHandlerMapping(FunctionCatalog catalog, FunctionController controller, FunctionProperties functionProperties) { - return new FunctionHandlerMapping(catalog, controller, functionProperties); - } - - @Bean - @ConditionalOnMissingBean - public StringConverter functionStringConverter(ConfigurableListableBeanFactory beanFactory) { - return new BasicStringConverter(beanFactory); - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java deleted file mode 100644 index 969ad6ca5..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.function; - -import java.lang.management.ManagementFactory; -import java.time.Duration; -import java.util.Collections; -import java.util.Set; -import java.util.function.Function; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.netty.DisposableServer; -import reactor.netty.http.server.HttpServer; - -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.web.ErrorProperties; -import org.springframework.boot.autoconfigure.web.WebProperties.Resources; -import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler; -import org.springframework.boot.web.reactive.error.DefaultErrorAttributes; -import org.springframework.boot.web.reactive.error.ErrorAttributes; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.FunctionalSpringApplication; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.context.config.ContextFunctionCatalogInitializer; -import org.springframework.cloud.function.web.constants.WebRequestConstants; -import org.springframework.cloud.function.web.util.FunctionWebRequestProcessingHelper; -import org.springframework.cloud.function.web.util.FunctionWrapper; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.event.SmartApplicationListener; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.env.Environment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.http.ResponseEntity; -import org.springframework.http.codec.ServerCodecConfigurer; -import org.springframework.http.server.reactive.HttpHandler; -import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.web.reactive.function.server.HandlerStrategies; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; -import org.springframework.web.reactive.function.server.ServerResponse.BodyBuilder; -import org.springframework.web.server.WebExceptionHandler; -import org.springframework.web.server.adapter.HttpWebHandlerAdapter; -import org.springframework.web.server.adapter.WebHttpHandlerBuilder; - -import static org.springframework.web.reactive.function.server.RequestPredicates.GET; -import static org.springframework.web.reactive.function.server.RequestPredicates.POST; -import static org.springframework.web.reactive.function.server.RouterFunctions.route; -import static org.springframework.web.reactive.function.server.ServerResponse.status; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - * @author Chris Bono - * @since 2.0 - */ -public class FunctionEndpointInitializer implements ApplicationContextInitializer { - - private static boolean webflux = ClassUtils - .isPresent("org.springframework.web.reactive.function.server.RouterFunction", null); - - @Override - public void initialize(GenericApplicationContext context) { - if (webflux && ContextFunctionCatalogInitializer.enabled - && context.getEnvironment().getProperty(FunctionalSpringApplication.SPRING_WEB_APPLICATION_TYPE, - WebApplicationType.class, WebApplicationType.REACTIVE) == WebApplicationType.REACTIVE - && context.getEnvironment().getProperty("spring.functional.enabled", Boolean.class, false)) { - registerEndpoint(context); - registerWebFluxAutoConfiguration(context); - } - } - - private void registerWebFluxAutoConfiguration(GenericApplicationContext context) { - context.registerBean(DefaultErrorWebExceptionHandler.class, () -> errorHandler(context)); - context.registerBean(WebHttpHandlerBuilder.WEB_HANDLER_BEAN_NAME, HttpWebHandlerAdapter.class, - () -> httpHandler(context)); - context.addApplicationListener(new ServerListener(context)); - } - - private void registerEndpoint(GenericApplicationContext context) { - context.registerBean(FunctionEndpointFactory.class, - () -> new FunctionEndpointFactory(context.getBean(FunctionProperties.class), context.getBean(FunctionCatalog.class), - context.getEnvironment())); - RouterFunctionRegister.register(context); - } - - private HttpWebHandlerAdapter httpHandler(GenericApplicationContext context) { - return (HttpWebHandlerAdapter) RouterFunctions.toHttpHandler(context.getBean(RouterFunction.class), - HandlerStrategies.empty().exceptionHandler(context.getBeansOfType(WebExceptionHandler.class).values().iterator().next()) - .codecs(config -> config.registerDefaults(true)).build()); - } - - private DefaultErrorWebExceptionHandler errorHandler(GenericApplicationContext context) { - context.registerBean(ErrorAttributes.class, () -> new DefaultErrorAttributes()); - context.registerBean(ErrorProperties.class, () -> new ErrorProperties()); - - context.registerBean(Resources.class, () -> new Resources()); - DefaultErrorWebExceptionHandler handler = new DefaultErrorWebExceptionHandler( - context.getBeansOfType(ErrorAttributes.class).values().iterator().next(), context.getBean(Resources.class), - context.getBean(ErrorProperties.class), context); - ServerCodecConfigurer codecs = ServerCodecConfigurer.create(); - handler.setMessageWriters(codecs.getWriters()); - handler.setMessageReaders(codecs.getReaders()); - return handler; - } - - private static class RouterFunctionRegister { - - private static void register(GenericApplicationContext context) { - context.registerBean(RouterFunction.class, - () -> context.getBean(FunctionEndpointFactory.class).functionEndpoints()); - } - - } - - private static class ServerListener implements SmartApplicationListener { - - private static Log logger = LogFactory.getLog(ServerListener.class); - - private GenericApplicationContext context; - - ServerListener(GenericApplicationContext context) { - this.context = context; - } - - @Override - public void onApplicationEvent(ApplicationEvent event) { - ApplicationContext context = ((ContextRefreshedEvent) event).getApplicationContext(); - if (context != this.context) { - return; - } - if (!ClassUtils.isPresent("org.springframework.http.server.reactive.HttpHandler", null)) { - logger.info("No web server classes found so no server to start"); - return; - } - Integer port = Integer.valueOf(context.getEnvironment().resolvePlaceholders("${server.port:${PORT:8080}}")); - String address = context.getEnvironment().resolvePlaceholders("${server.address:0.0.0.0}"); - if (port >= 0) { - HttpHandler handler = context.getBeansOfType(HttpHandler.class).values().iterator().next(); - ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler); - HttpServer httpServer = HttpServer.create().host(address).port(port).handle(adapter); - Thread thread = new Thread( - () -> httpServer.bindUntilJavaShutdown(Duration.ofSeconds(60), (server) -> callback(server, context)), - "server-startup"); - thread.setDaemon(false); - thread.start(); - } - } - - private void callback(DisposableServer server, ApplicationContext context) { - logger.info("HTTP server started on port: " + server.port()); - if (context instanceof ConfigurableApplicationContext) { - ((ConfigurableApplicationContext) context).getEnvironment().getPropertySources().addFirst( - new MapPropertySource("functionalServerProps", Collections.singletonMap("local.server.port", server.port()))); - } - try { - double uptime = ManagementFactory.getRuntimeMXBean().getUptime(); - logger.info("JVM running for " + uptime + "ms"); - } - catch (Throwable e) { - // ignore - } - } - - @Override - public boolean supportsEventType(Class eventType) { - return eventType.isAssignableFrom(ContextRefreshedEvent.class); - } - - } - -} - -class FunctionEndpointFactory { - - private static Log logger = LogFactory.getLog(FunctionEndpointFactory.class); - - private final FunctionCatalog functionCatalog; - - private final String handler; - - private final FunctionProperties functionProperties; - - FunctionEndpointFactory(FunctionProperties functionProperties, FunctionCatalog functionCatalog, Environment environment) { - String handler = environment.resolvePlaceholders("${function.handler}"); - if (handler.startsWith("$")) { - handler = null; - } - this.functionCatalog = functionCatalog; - this.handler = handler; - this.functionProperties = functionProperties; - } - - private FunctionInvocationWrapper extract(ServerRequest request) { - FunctionInvocationWrapper function; - if (handler != null) { - logger.info("Configured function: " + handler); - Set names = this.functionCatalog.getNames(Function.class); - Assert.isTrue(names.contains(handler), "Cannot locate function: " + handler); - function = this.functionCatalog.lookup(Function.class, handler); - } - else { - function = FunctionWebRequestProcessingHelper.findFunction(this.functionProperties, request.method(), functionCatalog, request.attributes(), - request.path()); - } - return function; - } - - @SuppressWarnings({ "unchecked" }) - public RouterFunction functionEndpoints() { - return route(POST("/**"), request -> { - FunctionInvocationWrapper funcWrapper = extract(request); - Class outputType = funcWrapper == null ? Object.class - : FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(funcWrapper.getOutputType())); - FunctionWrapper wrapper = new FunctionWrapper(funcWrapper); - Mono> stream = request.bodyToMono(String.class) - .flatMap(content -> (Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, content, false)); - - return stream.flatMap(entity -> { - BodyBuilder builder = status(entity.getStatusCode()).headers(headers -> headers.addAll(entity.getHeaders())); - if (outputType == null) { // consumer - return builder.build(); - } - else { - return builder.body(entity != null && entity.hasBody() ? Mono.just((T) entity.getBody()) : Mono.empty(), outputType); - } - }); - }).andRoute(GET("/**"), request -> { - FunctionInvocationWrapper funcWrapper = extract(request); - Class outputType = FunctionTypeUtils - .getRawType(FunctionTypeUtils.getGenericType(funcWrapper.getOutputType())); - if (funcWrapper.isSupplier()) { - Object result = FunctionWebRequestProcessingHelper.invokeFunction(funcWrapper, null, funcWrapper.isInputTypeMessage()); - if (!(result instanceof Publisher)) { - result = Mono.just(result); - } - return ServerResponse.ok().body(result, outputType); - } - else { - FunctionWrapper wrapper = new FunctionWrapper(funcWrapper); - - wrapper.setHeaders(request.headers().asHttpHeaders()); - String argument = (String) request.attribute(WebRequestConstants.ARGUMENT).get(); - wrapper.setArgument(Flux.just(argument)); - Object result = FunctionWebRequestProcessingHelper.invokeFunction(funcWrapper, wrapper.getArgument(), - funcWrapper.isInputTypeMessage()); - return ServerResponse.ok().body(result, outputType); - } - }); - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionController.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionController.java deleted file mode 100644 index 0e5977e7b..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionController.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2016-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.stream.Collectors; - -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.web.FunctionHttpProperties; -import org.springframework.cloud.function.web.constants.WebRequestConstants; -import org.springframework.cloud.function.web.util.FunctionWebRequestProcessingHelper; -import org.springframework.cloud.function.web.util.FunctionWrapper; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.http.ResponseEntity.BodyBuilder; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.context.request.ServletWebRequest; -import org.springframework.web.context.request.WebRequest; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest; - -/** - * @author Dave Syer - * @author Mark Fisher - * @author Oleg Zhurakousky - */ -@Component -@EnableConfigurationProperties(FunctionHttpProperties.class) -public class FunctionController { - - private final FunctionHttpProperties functionHttpProperties; - - public FunctionController(FunctionHttpProperties functionHttpProperties) { - this.functionHttpProperties = functionHttpProperties; - } - - @PostMapping(path = "/**", consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE, - MediaType.MULTIPART_FORM_DATA_VALUE }) - @ResponseBody - public Object form(WebRequest request) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - if (((ServletWebRequest) request).getRequest() instanceof StandardMultipartHttpServletRequest) { - MultiValueMap multiFileMap = ((StandardMultipartHttpServletRequest) ((ServletWebRequest) request) - .getRequest()).getMultiFileMap(); - if (!CollectionUtils.isEmpty(multiFileMap)) { - List> files = multiFileMap.values().stream().flatMap(v -> v.stream()) - .map(file -> MessageBuilder.withPayload(file).copyHeaders(wrapper.getHeaders()).build()) - .collect(Collectors.toList()); - FunctionInvocationWrapper function = wrapper.getFunction(); - - Publisher result = (Publisher) function.apply(Flux.fromIterable(files)); - BodyBuilder builder = ResponseEntity.ok(); - if (result instanceof Flux) { - result = Flux.from(result).map(message -> { - return message instanceof Message ? ((Message) message).getPayload() : message; - }).collectList(); - } - return Mono.from(result).flatMap(body -> Mono.just(builder.body(body))); - } - } - return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getParams(), false); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); - } - } - - @SuppressWarnings("unchecked") - @PostMapping(path = "/**", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - @ResponseBody - public Mono>> postStream(WebRequest request, - @RequestBody(required = false) String body) { - String argument = StringUtils.hasText(body) ? body : ""; - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return ((Mono>) FunctionWebRequestProcessingHelper.processRequest(wrapper, argument, true)).map(response -> ResponseEntity.ok() - .headers(response.getHeaders()).body((Publisher) response.getBody())); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); - } - } - - @GetMapping(path = "/**", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - @ResponseBody - public Publisher getStream(WebRequest request) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), true); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition())); - } - } - - @PostMapping(path = "/**") - @ResponseBody - public Object post(WebRequest request, @RequestBody(required = false) String body) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("POST", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - Assert.isTrue(!wrapper.getFunction().isSupplier(), "'POST' can only be mapped to Function or Consumer"); - return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("POST", wrapper.getFunction().getFunctionDefinition())); - } - } - - @PutMapping(path = "/**") - @ResponseBody - public Object put(WebRequest request, @RequestBody(required = false) String body) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("PUT", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return FunctionWebRequestProcessingHelper.processRequest(wrapper, body, false); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("PUT", wrapper.getFunction().getFunctionDefinition())); - } - } - - @DeleteMapping(path = "/**") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete(WebRequest request, @RequestBody(required = false) String body) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("DELETE", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - Assert.isTrue(wrapper.getFunction().isConsumer(), "'DELETE' can only be mapped to Consumer"); - FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("DELETE", wrapper.getFunction().getFunctionDefinition())); - } - } - - @GetMapping(path = "/**") - @ResponseBody - public Object get(WebRequest request) { - FunctionWrapper wrapper = wrapper(request); - if (FunctionWebRequestProcessingHelper.isFunctionValidForMethod("GET", wrapper.getFunction().getFunctionDefinition(), this.functionHttpProperties)) { - return FunctionWebRequestProcessingHelper.processRequest(wrapper, wrapper.getArgument(), false); - } - else { - throw new IllegalArgumentException(FunctionWebRequestProcessingHelper.buildBadMappingErrorMessage("GET", wrapper.getFunction().getFunctionDefinition())); - } - } - - private FunctionWrapper wrapper(WebRequest request) { - FunctionInvocationWrapper function = (FunctionInvocationWrapper) request - .getAttribute(WebRequestConstants.HANDLER, WebRequest.SCOPE_REQUEST); - FunctionWrapper wrapper = new FunctionWrapper(function, (((ServletWebRequest) request).getRequest()).getMethod()); - for (String key : request.getParameterMap().keySet()) { - wrapper.getParams().addAll(key, Arrays.asList(request.getParameterValues(key))); - } - for (Iterator keys = request.getHeaderNames(); keys.hasNext();) { - String key = keys.next(); - wrapper.getHeaders().addAll(key, Arrays.asList(request.getHeaderValues(key))); - } - - HttpHeaders headers = HttpHeaders.writableHttpHeaders(wrapper.getHeaders()); - headers.set("uri", ((ServletWebRequest) request).getRequest().getRequestURI()); - - String argument = (String) request.getAttribute(WebRequestConstants.ARGUMENT, - WebRequest.SCOPE_REQUEST); - if (argument != null) { - wrapper.setArgument(argument); - } - return wrapper; - } -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionHandlerMapping.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionHandlerMapping.java deleted file mode 100644 index 5ad188ea3..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/FunctionHandlerMapping.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.util.HashMap; - -import jakarta.servlet.http.HttpServletRequest; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.web.constants.WebRequestConstants; -import org.springframework.cloud.function.web.util.FunctionWebRequestProcessingHelper; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; -import org.springframework.util.StringUtils; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - - - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -@Configuration -@ConditionalOnClass(RequestMappingHandlerMapping.class) -public class FunctionHandlerMapping extends RequestMappingHandlerMapping - implements InitializingBean { - - private final FunctionCatalog functions; - - private final FunctionController controller; - - private final FunctionProperties functionProperties; - - @Value("${spring.cloud.function.web.path:}") - private String prefix = ""; - - @Autowired - public FunctionHandlerMapping(FunctionProperties functionProperties, FunctionCatalog catalog, - FunctionController controller) { - this.functions = catalog; - this.logger.info("FunctionCatalog: " + catalog); - setOrder(super.getOrder() - 5); - this.controller = controller; - this.functionProperties = functionProperties; - } - - @Override - public void afterPropertiesSet() { - super.afterPropertiesSet(); - detectHandlerMethods(this.controller); - while (this.prefix.endsWith("/")) { - this.prefix = this.prefix.substring(0, this.prefix.length() - 1); - } - } - - @Override - protected void initHandlerMethods() { - } - - @Override - protected HandlerMethod getHandlerInternal(HttpServletRequest request) - throws Exception { - HandlerMethod handler = super.getHandlerInternal(request); - if (handler == null) { - return null; - } - String path = (String) request - .getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); - if (path == null) { - return handler; - } - if (StringUtils.hasText(this.prefix) && !path.startsWith(this.prefix)) { - return null; - } - if (path.startsWith(this.prefix)) { - path = path.substring(this.prefix.length()); - } - - Object function = FunctionWebRequestProcessingHelper.findFunction(this.functionProperties, HttpMethod.valueOf(request.getMethod()), - this.functions, new HttpRequestAttributeDelegate(request), path); - if (function != null) { - if (this.logger.isDebugEnabled()) { - this.logger.debug("Found function for GET: " + path); - } - request.setAttribute(WebRequestConstants.HANDLER, function); - return handler; - } - return null; - } - - @SuppressWarnings("serial") - private static class HttpRequestAttributeDelegate extends HashMap { - private final HttpServletRequest request; - HttpRequestAttributeDelegate(HttpServletRequest request) { - this.request = request; - } - - @Override - public Object put(String key, Object value) { - this.request.setAttribute(key, value); - return value; - } - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/ReactorAutoConfiguration.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/ReactorAutoConfiguration.java deleted file mode 100644 index 4c9666f55..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/mvc/ReactorAutoConfiguration.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.web.BasicStringConverter; -import org.springframework.cloud.function.web.StringConverter; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; - -/** - * @author Dave Syer - * @author Mark Fisher - * @author Oleg Zhurakousky - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnWebApplication(type = Type.SERVLET) -@ConditionalOnClass({ Flux.class, AsyncHandlerMethodReturnValueHandler.class }) -@Import({ FunctionController.class}) -public class ReactorAutoConfiguration { - - @Bean - public FunctionHandlerMapping functionHandlerMapping(FunctionProperties functionProperties, FunctionCatalog catalog, FunctionController controller) { - return new FunctionHandlerMapping(functionProperties, catalog, controller); - } - - @Bean - @ConditionalOnMissingBean - public StringConverter functionStringConverter(ConfigurableListableBeanFactory beanFactory) { - return new BasicStringConverter(beanFactory); - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/DestinationResolver.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/DestinationResolver.java deleted file mode 100644 index 3479e49a4..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/DestinationResolver.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.util.function.Supplier; - -/** - * @author Dave Syer - * - */ -public interface DestinationResolver { - - String destination(Supplier supplier, String name, Object value); - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/ExporterProperties.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/ExporterProperties.java deleted file mode 100644 index a7c32f9bb..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/ExporterProperties.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.function.context.FunctionProperties; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - * - */ -@ConfigurationProperties(prefix = FunctionProperties.PREFIX + ".web.export") -public class ExporterProperties { - - /** - * Flag to indicate that the supplier emits HTTP requests automatically on startup. - */ - private boolean autoStartup = true; - - /** - * Flag to indicate that extra logging is required for the supplier. - */ - private boolean debug = true; - - /** - * Properties related to a source of items (via an HTTP GET on startup). - */ - private Source source = new Source(); - - /** - * Properties related to a sink of items (via an HTTP POST on startup). - */ - private Sink sink = new Sink(); - - /** - * Flag to enable the export of a supplier. - */ - private boolean enabled; - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public boolean isAutoStartup() { - return this.autoStartup; - } - - public void setAutoStartup(boolean autoStartup) { - this.autoStartup = autoStartup; - } - - public boolean isDebug() { - return this.debug; - } - - public void setDebug(boolean debug) { - this.debug = debug; - } - - public Source getSource() { - return this.source; - } - - public Sink getSink() { - return this.sink; - } - - public static class Source { - - /** - * URL template for creating a virtual Supplier from HTTP GET. - */ - private String url; - - /** - * If the origin url is set, the type of content expected (e.g. a POJO class). - * Defaults to String. - */ - private Class type; - - /** - * Include the incoming headers in the outgoing Supplier. If true the supplier - * will be of generic type Message of T equal to the source type. - */ - private boolean includeHeaders = true; - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - - public Class getType() { - return this.type == null ? String.class : this.type; - } - - public void setType(Class type) { - this.type = type; - } - - public void setIncludeHeaders(boolean includeHeaders) { - this.includeHeaders = includeHeaders; - } - - public boolean isIncludeHeaders() { - return this.includeHeaders; - } - - } - - public static class Sink { - - /** - * URL template for outgoing HTTP requests. Each item from the supplier is POSTed - * to this target. - */ - private String url; - - /** - * Additional headers to append to the outgoing HTTP requests. - */ - private Map headers = new LinkedHashMap<>(); - - /** - * The name of a specific existing Supplier to export from the function catalog. - */ - private String name; - - /** - * Content type to use when serializing source's output for transport (default 'application/json`). - */ - private String contentType = "application/json"; - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - - public Map getHeaders() { - return this.headers; - } - - public String getContentType() { - return contentType; - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java deleted file mode 100644 index 9d65de859..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.lang.reflect.Type; -import java.util.function.Supplier; - -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.web.source.FunctionExporterAutoConfiguration.SourceActiveCondition; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.ResolvableType; -import org.springframework.core.env.Environment; -import org.springframework.web.reactive.function.client.WebClient; - -/** - * @author Dave Syer - * - */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass(WebClient.class) -@Conditional(SourceActiveCondition.class) -@EnableConfigurationProperties(ExporterProperties.class) -public class FunctionExporterAutoConfiguration { - - private ExporterProperties props; - - @Autowired - FunctionExporterAutoConfiguration(ExporterProperties props) { - this.props = props; - } - - @Bean - @ConditionalOnProperty(prefix = "spring.cloud.function.web.export.sink", name = "url") - public SupplierExporter sourceForwarder(RequestBuilder requestBuilder, DestinationResolver destinationResolver, - FunctionCatalog catalog, WebClient.Builder builder) { - return new SupplierExporter(requestBuilder, destinationResolver, catalog, builder.build(), this.props); - } - - @Bean - @ConditionalOnProperty(prefix = "spring.cloud.function.web.export.source", name = "url") - public FunctionRegistration>> origin(WebClient.Builder builder) { - HttpSupplier supplier = new HttpSupplier(builder.build(), this.props); - FunctionRegistration>> registration = new FunctionRegistration<>(supplier); - Type rawType = ResolvableType.forClassWithGenerics(Supplier.class, this.props.getSource().getType()).getType(); -// FunctionType functionType = FunctionType.supplier(this.props.getSource().getType()).wrap(Flux.class); -// FunctionType type = FunctionType.of(rawType); -// if (this.props.getSource().isIncludeHeaders()) { -//// type = type.message(); -// } - Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(HttpSupplier.class); - registration = registration.type(type); - return registration; - } - - @Bean - public RequestBuilder simpleRequestBuilder(Environment environment) { - SimpleRequestBuilder builder = new SimpleRequestBuilder(environment); - if (this.props.getSink().getUrl() != null) { - builder.setTemplateUrl(this.props.getSink().getUrl()); - } - builder.setHeaders(this.props.getSink().getHeaders()); - return builder; - } - - @Bean - @ConditionalOnMissingBean - public DestinationResolver simpleDestinationResolver() { - return new SimpleDestinationResolver(); - } - - static class SourceActiveCondition extends AnyNestedCondition { - - SourceActiveCondition() { - super(ConfigurationPhase.PARSE_CONFIGURATION); - } - - @ConditionalOnNotWebApplication - static class OnNotWebapp { - - } - - @ConditionalOnProperty(prefix = "spring.cloud.function.web.export", name = "enabled", matchIfMissing = true) - static class Enabled { - - } - - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterInitializer.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterInitializer.java deleted file mode 100644 index 41e906d1d..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterInitializer.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.util.function.Supplier; - -import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebEnvironment; -import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext; -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.config.ContextFunctionCatalogInitializer; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.util.ClassUtils; -import org.springframework.web.context.ConfigurableWebEnvironment; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.reactive.function.client.WebClient; -import org.springframework.web.reactive.function.client.WebClient.Builder; - -/** - * @author Dave Syer - * @since 2.0 - * - */ -class FunctionExporterInitializer implements ApplicationContextInitializer { - - @Override - public void initialize(GenericApplicationContext context) { - if (ContextFunctionCatalogInitializer.enabled - && context.getEnvironment().getProperty("spring.functional.enabled", Boolean.class, false) - && isExporting(context)) { - registerWebClient(context); - registerExport(context); - } - } - - private void registerWebClient(GenericApplicationContext context) { - if (ClassUtils.isPresent("org.springframework.web.reactive.function.client.WebClient", - getClass().getClassLoader())) { - if (context.getBeanFactory().getBeanNamesForType(WebClient.Builder.class, false, false).length == 0) { - context.registerBean(WebClient.Builder.class, new Supplier() { - @Override - public Builder get() { - return WebClient.builder(); - } - }); - } - } - } - - private boolean isExporting(GenericApplicationContext context) { - Boolean enabled = context.getEnvironment().getProperty("spring.cloud.function.web.export.enabled", - Boolean.class); - if (enabled != null) { - return enabled; - } - if (ClassUtils.isPresent("org.springframework.web.context.WebApplicationContext", - getClass().getClassLoader())) { - if (context instanceof WebApplicationContext || context instanceof ReactiveWebApplicationContext - || context.getEnvironment() instanceof ConfigurableWebEnvironment - || context.getEnvironment() instanceof ConfigurableReactiveWebEnvironment) { - return false; - } - } - return true; - } - - private void registerExport(GenericApplicationContext context) { - context.registerBean(ExporterProperties.class, () -> new ExporterProperties()); - context.registerBean(FunctionExporterAutoConfiguration.class, - () -> new FunctionExporterAutoConfiguration(context.getBean(ExporterProperties.class))); - if (context.getBeanFactory().getBeanNamesForType(DestinationResolver.class, false, false).length == 0) { - context.registerBean(DestinationResolver.class, - () -> context.getBean(FunctionExporterAutoConfiguration.class).simpleDestinationResolver()); - } - if (context.getBeanFactory().getBeanNamesForType(RequestBuilder.class, false, false).length == 0) { - context.registerBean(RequestBuilder.class, () -> context.getBean(FunctionExporterAutoConfiguration.class) - .simpleRequestBuilder(context.getEnvironment())); - } - if (context.getEnvironment().getProperty("spring.cloud.function.web.export.source.url") != null) { - context.registerBean("origin", FunctionRegistration.class, () -> context - .getBean(FunctionExporterAutoConfiguration.class).origin(context.getBean(WebClient.Builder.class))); - } - if (context.getEnvironment().getProperty("spring.cloud.function.web.export.sink.url") != null) { - context.registerBean(SupplierExporter.class, - () -> context.getBean(FunctionExporterAutoConfiguration.class).sourceForwarder( - context.getBean(RequestBuilder.class), context.getBean(DestinationResolver.class), - context.getBean(FunctionCatalog.class), context.getBean(WebClient.Builder.class))); - } - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/HttpSupplier.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/HttpSupplier.java deleted file mode 100644 index e22ad1929..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/HttpSupplier.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2018-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.time.Duration; -import java.util.function.Supplier; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.cloud.function.web.util.HeaderUtils; -import org.springframework.http.HttpStatusCode; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.WebClient; - -/** - * A {@link Supplier} that pulls data from an HTTP endpoint. Repeatedly polls the endpoint - * until a non-2xx response is received, at which point it will repeatedly produced a - * Mono at 1 sec intervals until the next 2xx response. - * - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public class HttpSupplier implements Supplier> { - - private static Log logger = LogFactory.getLog(HttpSupplier.class); - - private WebClient client; - - private ExporterProperties props; - - /** - * @param client the WebClient to use. The baseUrl should be set. - * @param props the ExporterProperties to use to parameterize the requests. - */ - public HttpSupplier(WebClient client, ExporterProperties props) { - this.client = client; - this.props = props; - } - - @Override - public Flux get() { - return get(this.client); - } - - private Flux get(WebClient client) { - Flux result = client.get().uri(this.props.getSource().getUrl()).exchange() - .flatMap(this::transform).repeat(); - if (this.props.isDebug()) { - result = result.log(); - } - return result.onErrorResume(TerminateException.class, error -> Mono.empty()); - } - - private Mono transform(ClientResponse response) { - HttpStatusCode status = response.statusCode(); - if (!status.is2xxSuccessful()) { - if (this.props.isDebug()) { - logger.info("Delaying supplier based on status=" + response.statusCode()); - } - return Mono.delay(Duration.ofSeconds(1)); - } - return response.bodyToMono(this.props.getSource().getType()) - .map(value -> message(response, value)); - } - - private Object message(ClientResponse response, Object payload) { - if (!this.props.getSource().isIncludeHeaders()) { - return payload; - } - return MessageBuilder.withPayload(payload) - .copyHeaders(HeaderUtils.fromHttp( - HeaderUtils.sanitize(response.headers().asHttpHeaders()))) - .setHeader("scf-sink-url", this.props.getSink().getUrl()) - .setHeader("scf-func-name", this.props.getSink().getName()) - .build(); - } - - @SuppressWarnings("serial") - private static class TerminateException extends RuntimeException { - - @SuppressWarnings("unused") - TerminateException() { - super("Planned termination"); - } - - @Override - public synchronized Throwable fillInStackTrace() { - return this; - } - - } -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/RequestBuilder.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/RequestBuilder.java deleted file mode 100644 index cb0dd48cd..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/RequestBuilder.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.net.URI; - -import org.springframework.http.HttpHeaders; - -/** - * @author Dave Syer - * - */ -public interface RequestBuilder { - - URI uri(String destination); - - HttpHeaders headers(String destination, Object value); - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleDestinationResolver.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleDestinationResolver.java deleted file mode 100644 index 3426c757c..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleDestinationResolver.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.util.function.Supplier; - -/** - * @author Dave Syer - * - */ -public class SimpleDestinationResolver implements DestinationResolver { - - @Override - public String destination(Supplier supplier, String name, Object value) { - return name.contains("|") ? name.substring(0, name.indexOf("|")).trim() : name; - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleRequestBuilder.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleRequestBuilder.java deleted file mode 100644 index abc3f600f..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SimpleRequestBuilder.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.cloud.function.web.util.HeaderUtils; -import org.springframework.core.env.Environment; -import org.springframework.http.HttpHeaders; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; - -/** - * @author Dave Syer - * - */ -class SimpleRequestBuilder implements RequestBuilder { - - private String baseUrl = "http://${destination}"; - - private Map headers = new LinkedHashMap<>(); - - private Environment environment; - - SimpleRequestBuilder(Environment environment) { - this.environment = environment; - } - - @Override - public HttpHeaders headers(String destination, Object value) { - MessageHeaders incoming = new MessageHeaders(Collections.emptyMap()); - if (value instanceof Message) { - Message message = (Message) value; - incoming = message.getHeaders(); - } - HttpHeaders result = HeaderUtils.fromMessage(incoming); - for (String key : this.headers.keySet()) { - String header = this.headers.get(key); - header = header.replace("${destination}", destination); - header = this.environment.resolvePlaceholders(header); - result.set(key, header); - } - return result; - } - - @Override - public URI uri(String destination) { - try { - return new URI(this.baseUrl.replace("${destination}", destination) - .replace("{{destination}}", destination)); - } - catch (URISyntaxException e) { - throw new IllegalStateException("Cannot create URI", e); - } - } - - public void setTemplateUrl(String baseUrl) { - this.baseUrl = baseUrl; - } - - public void setHeaders(Map headers) { - this.headers.putAll(headers); - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SupplierExporter.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SupplierExporter.java deleted file mode 100644 index 8a24e8455..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/SupplierExporter.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.net.URI; -import java.time.Duration; -import java.util.Collections; -import java.util.Set; -import java.util.function.Supplier; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.Disposable; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.util.retry.Retry; - -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.context.SmartLifecycle; -import org.springframework.http.HttpHeaders; -import org.springframework.messaging.Message; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.WebClient; - -/** - * Forwards items obtained from a {@link Supplier} or set of suppliers to an external HTTP - * endpoint. - * - * @author Dave Syer - * @author Oleg Zhurakousky - * - */ -public class SupplierExporter implements SmartLifecycle { - - private static Log logger = LogFactory.getLog(SupplierExporter.class); - - private final FunctionCatalog catalog; - - private final WebClient client; - - private final DestinationResolver destinationResolver; - - private final RequestBuilder requestBuilder; - - private final String supplier; - - private final String contentType; - - private volatile boolean running; - - private volatile boolean ok = true; - - private boolean autoStartup = true; - - private boolean debug = true; - - private volatile Disposable subscription; - - SupplierExporter(RequestBuilder requestBuilder, - DestinationResolver destinationResolver, FunctionCatalog catalog, - WebClient client, ExporterProperties exporterProperties) { - this.requestBuilder = requestBuilder; - this.destinationResolver = destinationResolver; - this.catalog = catalog; - this.client = client; - this.debug = exporterProperties.isDebug(); - this.autoStartup = exporterProperties.isAutoStartup(); - this.supplier = exporterProperties.getSink().getName(); - this.contentType = exporterProperties.getSink().getContentType(); - } - - @Override - public void start() { - if (this.running) { - return; - } - logger.info("Starting"); - - Flux streams = Flux.empty(); - Set names = this.supplier == null ? this.catalog.getNames(Supplier.class) - : Collections.singleton(this.supplier); - - boolean suppliersPresent = false; - for (String name : names) { - Supplier> supplier = this.catalog.lookup(name, this.contentType); - if (supplier == null) { - logger.warn("No such Supplier: " + name); - continue; - } - streams = streams.mergeWith(forward(supplier, name)); - suppliersPresent = true; - } - if (suppliersPresent) { - this.subscription = streams - .retryWhen(Retry.backoff(5, Duration.ofSeconds(1))) -// .retry(error -> { -// /* -// * The ConnectException may happen if a server is not yet available/reachable -// * The ClassCast is to handle delayed Mono issued by HttpSupplier.transform for non-2xx responses -// */ -// boolean retry = error instanceof ConnectException || error instanceof ClassCastException -// && this.running; -// if (!retry) { -// this.ok = false; -// if (!this.debug) { -// logger.info(error); -// } -// stop(); -// } -// return retry; -// } -// ) - .doOnComplete(() -> { - stop(); - }) - .subscribe(); - - this.ok = true; - this.running = true; - } - } - - public boolean isOk() { - return this.ok; - } - - @Override - public void stop() { - logger.info("Stopping"); - this.running = false; - this.subscription.dispose(); - } - - @Override - public boolean isRunning() { - return this.running; - } - - @Override - public int getPhase() { - return 0; - } - - @Override - public boolean isAutoStartup() { - return this.autoStartup; - } - - @Override - public void stop(Runnable callback) { - stop(); - callback.run(); - } - - private Flux forward(Supplier> supplier, String name) { - Flux o = (Flux) supplier.get(); -// o.subscribe(v -> { -// System.out.println(v); -// }); - return Flux.from(o).flatMap(value -> { - String destination = this.destinationResolver.destination(supplier, name, value); - if (this.debug) { - logger.info("Posting to: " + destination); - } - return post(uri(destination), destination, value); - }); - } - - private Mono post(URI uri, String destination, Object value) { - Object body = value; - if (value instanceof Message) { - Message message = (Message) value; - body = message.getPayload(); - } - if (this.debug) { - logger.debug("Sending BODY as type: " + body.getClass().getName()); - } - Mono result = this.client.post().uri(uri) - .headers(headers -> headers(headers, destination, value)).bodyValue(body) - .exchange() - .doOnNext(response -> { - if (this.debug) { - logger.debug("Response STATUS: " + response.statusCode()); - } - }); - if (this.debug) { - result = result.log(); - } - return result; - } - - private void headers(HttpHeaders headers, String destination, Object value) { - headers.putAll(this.requestBuilder.headers(destination, value)); - } - - private URI uri(String destination) { - return this.requestBuilder.uri(destination); - } -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWebRequestProcessingHelper.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWebRequestProcessingHelper.java deleted file mode 100644 index 2f92cb1b8..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWebRequestProcessingHelper.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2019-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.util; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.cloud.function.web.FunctionHttpProperties; -import org.springframework.cloud.function.web.constants.WebRequestConstants; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.http.ResponseEntity.BodyBuilder; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * !INTERNAL USE ONLY! - * - * @author Oleg Zhurakousky - * - */ -public final class FunctionWebRequestProcessingHelper { - - private static Log logger = LogFactory.getLog(FunctionWebRequestProcessingHelper.class); - - private FunctionWebRequestProcessingHelper() { - - } - - public static FunctionInvocationWrapper findFunction(FunctionProperties functionProperties, HttpMethod method, FunctionCatalog functionCatalog, - Map attributes, String path) { - if (method.equals(HttpMethod.GET) || method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.DELETE)) { - return doFindFunction(functionProperties.getDefinition(), method, functionCatalog, attributes, path); - } - else { - throw new IllegalStateException("HTTP method '" + method + "' is not supported;"); - } - } - - public static Object invokeFunction(FunctionInvocationWrapper function, Object input, boolean isMessage) { - Object result = function.apply(input); - return postProcessResult(result, isMessage); - } - - public static boolean isFunctionValidForMethod(String httpMethod, String functionDefinition, FunctionHttpProperties functionHttpProperties) { - String functionDefinitions = null; - switch (httpMethod) { - case "GET": - functionDefinitions = functionHttpProperties.getGet(); - break; - case "POST": - functionDefinitions = functionHttpProperties.getPost(); - break; - case "PUT": - functionDefinitions = functionHttpProperties.getPut(); - break; - case "DELETE": - functionDefinitions = functionHttpProperties.getDelete(); - break; - default: - return false; - } - if (StringUtils.hasText(functionDefinitions)) { - return Arrays.asList(functionDefinitions.split(";")).contains(functionDefinition); - } - return true; - } - - public static String buildBadMappingErrorMessage(String httpMethod, String functionDefinition) { - return "Function '" + functionDefinition + "' is not eligible to be invoked " - + "via " + httpMethod + " method. This is due to the fact that explicit mappings for " + httpMethod - + " are provided via 'spring.cloud.function.http." + httpMethod + "' property " - + "and this function is not listed there. Either remove all explicit mappings for " + httpMethod + " or add this function to the list of functions " - + "specified in 'spring.cloud.function.http." + httpMethod + "' property."; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static Publisher processRequest(FunctionWrapper wrapper, Object argument, boolean eventStream) { - if (argument == null) { - argument = ""; - } - FunctionInvocationWrapper function = wrapper.getFunction(); - - if (function == null) { - return Mono.just(ResponseEntity.notFound().build()); - } - - HttpHeaders headers = wrapper.getHeaders(); - - Message inputMessage = null; - - - MessageBuilder builder = MessageBuilder.withPayload(argument); - if (!CollectionUtils.isEmpty(wrapper.getParams())) { - builder = builder.setHeader(HeaderUtils.HTTP_REQUEST_PARAM, wrapper.getParams().toSingleValueMap()); - } - inputMessage = builder.copyHeaders(headers.toSingleValueMap()).build(); - - if (function.isRoutingFunction()) { - function.setSkipOutputConversion(true); - } - - Object result = function.apply(inputMessage); - if (function.isConsumer()) { - if (result instanceof Publisher) { - Mono.from((Publisher) result).subscribe(); - } - return "DELETE".equals(wrapper.getMethod()) ? - Mono.empty() : Mono.just(ResponseEntity.accepted().headers(HeaderUtils.sanitize(headers)).build()); - } - - BodyBuilder responseOkBuilder = ResponseEntity.ok().headers(HeaderUtils.sanitize(headers)); - - Publisher pResult; - if (result instanceof Publisher) { - pResult = (Publisher) result; - if (eventStream) { - return Flux.from(pResult); - } - - if (pResult instanceof Flux) { - pResult = ((Flux) pResult).onErrorContinue((e, v) -> { - logger.error("Failed to process value: " + v, (Throwable) e); - }).collectList(); - } - pResult = Mono.from(pResult); - } - else { - pResult = Mono.just(result); - } - - return Mono.from(pResult).map(v -> { - if (v instanceof Iterable i) { - List aggregatedResult = (List) StreamSupport.stream(i.spliterator(), false).map(m -> { - return m instanceof Message ? processMessage(responseOkBuilder, (Message) m) : m; - }).collect(Collectors.toList()); - return responseOkBuilder.header("content-type", "application/json").body(aggregatedResult); - } - else if (v instanceof Message) { - return responseOkBuilder.body(processMessage(responseOkBuilder, (Message) v)); - } - else { - return responseOkBuilder.body(v); - } - }); - } - - private static Object processMessage(BodyBuilder responseOkBuilder, Message message) { - responseOkBuilder.headers(HeaderUtils.fromMessage(message.getHeaders())); - return message.getPayload(); - } - - private static FunctionInvocationWrapper doFindFunction(String functionDefinition, HttpMethod method, FunctionCatalog functionCatalog, - Map attributes, String path) { - - path = path.startsWith("/") ? path.substring(1) : path; - if (method.equals(HttpMethod.GET)) { - FunctionInvocationWrapper function = functionCatalog.lookup(path); - if (function != null && function.isSupplier()) { - attributes.put(WebRequestConstants.SUPPLIER, function); - return function; - } - } - - StringBuilder builder = new StringBuilder(); - String name = path; - String value = null; - for (String element : path.split("/")) { - if (builder.length() > 0) { - builder.append("/"); - } - builder.append(element); - name = builder.toString(); - value = path.length() > name.length() ? path.substring(name.length() + 1) - : null; - FunctionInvocationWrapper function = functionCatalog.lookup(name); - if (function != null) { - return postProcessFunction(function, value, attributes); - } - } - - if (StringUtils.hasText(functionDefinition)) { - FunctionInvocationWrapper function = functionCatalog.lookup(functionDefinition); - if (function != null) { - return postProcessFunction(function, value, attributes); - } - } - return null; - } - - private static FunctionInvocationWrapper postProcessFunction(FunctionInvocationWrapper function, String argument, Map attributes) { - attributes.put(WebRequestConstants.FUNCTION, function); - if (argument != null) { - attributes.put(WebRequestConstants.ARGUMENT, argument); - } - return function; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static Object postProcessResult(Object result, boolean isMessage) { - if (result instanceof Flux) { - result = ((Flux) result).map(v -> postProcessResult(v, isMessage)); - } - else if (result instanceof Mono) { - result = ((Mono) result).map(v -> postProcessResult(v, isMessage)); - } - else if (result instanceof Message) { - if (((Message) result).getPayload() instanceof byte[]) { - String str = new String((byte[]) ((Message) result).getPayload()); - result = MessageBuilder.withPayload(str).copyHeaders(((Message) result).getHeaders()).build(); - } - } - - if (result instanceof byte[]) { - result = new String((byte[]) result); - } - return result; - } - -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWrapper.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWrapper.java deleted file mode 100644 index d9b6af26d..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/FunctionWrapper.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2021-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.util; - -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; -import org.springframework.http.HttpHeaders; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -/** - * For internal use only. - * - * - * @author Oleg Zhurakousky - * - */ -public class FunctionWrapper { - private final FunctionInvocationWrapper function; - - private final MultiValueMap params = new LinkedMultiValueMap<>(); - - private HttpHeaders headers = new HttpHeaders(); - - private Object argument; - - private final String method; - - /** - * - * @param function instance of {@link FunctionInvocationWrapper} - * @deprecated since 4.0.4 in favor of the constructor that takes Http method as second argument. - */ - @Deprecated - public FunctionWrapper(FunctionInvocationWrapper function) { - this(function, null); - } - - public FunctionWrapper(FunctionInvocationWrapper function, String method) { - this.function = function; - this.method = method; - } - - public HttpHeaders getHeaders() { - return headers; - } - - public void setHeaders(HttpHeaders headers) { - this.headers = headers; - } - - public Object getArgument() { - return argument; - } - - public void setArgument(Object argument) { - this.argument = argument; - } - - public FunctionInvocationWrapper getFunction() { - return function; - } - - public MultiValueMap getParams() { - return params; - } - - public String getMethod() { - return method; - } -} diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/HeaderUtils.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/HeaderUtils.java deleted file mode 100644 index f16c1afb2..000000000 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/HeaderUtils.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.util; - -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.http.HttpHeaders; -import org.springframework.messaging.MessageHeaders; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -public final class HeaderUtils { - - /** - * Message Header name which contains HTTP request parameters. - */ - public static final String HTTP_REQUEST_PARAM = "http_request_param"; - - private static HttpHeaders IGNORED = new HttpHeaders(); - - private static HttpHeaders REQUEST_ONLY = new HttpHeaders(); - - static { - IGNORED.add(MessageHeaders.ID, ""); - IGNORED.add(HttpHeaders.CONTENT_LENGTH, "0"); - // Headers that would typically be added by a downstream client - REQUEST_ONLY.add(HttpHeaders.ACCEPT, ""); - REQUEST_ONLY.add(HttpHeaders.CONTENT_LENGTH, ""); - REQUEST_ONLY.add(HttpHeaders.CONTENT_TYPE, ""); - REQUEST_ONLY.add(HttpHeaders.HOST, ""); - } - - private HeaderUtils() { - throw new IllegalStateException("Can't instantiate a utility class"); - } - - public static HttpHeaders fromMessage(MessageHeaders headers) { - HttpHeaders result = new HttpHeaders(); - for (String name : headers.keySet()) { - Object value = headers.get(name); - name = name.toLowerCase(); - if (!IGNORED.containsKey(name)) { - Collection values = multi(value); - for (Object object : values) { - result.set(name, object.toString()); - } - } - } - return result; - } - - public static HttpHeaders sanitize(HttpHeaders request) { - HttpHeaders result = new HttpHeaders(); - for (String name : request.keySet()) { - List value = request.get(name); - name = name.toLowerCase(); - if (!IGNORED.containsKey(name) && !REQUEST_ONLY.containsKey(name)) { - result.put(name, value); - } - } - return result; - } - - public static MessageHeaders fromHttp(HttpHeaders headers) { - Map map = new LinkedHashMap<>(); - for (String name : headers.keySet()) { - Collection values = multi(headers.get(name)); - name = name.toLowerCase(); - Object value = values == null ? null - : (values.size() == 1 ? values.iterator().next() : values); - if (name.toLowerCase().equals(HttpHeaders.CONTENT_TYPE.toLowerCase())) { - name = MessageHeaders.CONTENT_TYPE; - } - map.put(name, value); - } - return new MessageHeaders(map); - } - - private static Collection multi(Object value) { - return value instanceof Collection ? (Collection) value : Arrays.asList(value); - } - -} diff --git a/spring-cloud-function-web/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-function-web/src/main/resources/META-INF/additional-spring-configuration-metadata.json deleted file mode 100644 index 217d1affe..000000000 --- a/spring-cloud-function-web/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "properties": [ - { - "name": "spring.cloud.function.web.path", - "type": "java.lang.String", - "description": "Path to web resources for functions (should start with / if not empty).", - "defaultValue": "" - } - ] -} - diff --git a/spring-cloud-function-web/src/main/resources/META-INF/spring.factories b/spring-cloud-function-web/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 71cbc9fa1..000000000 --- a/spring-cloud-function-web/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,6 +0,0 @@ -org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc=\ -org.springframework.cloud.function.web.flux.ReactorAutoConfiguration,\ -org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration -org.springframework.context.ApplicationContextInitializer=\ -org.springframework.cloud.function.web.function.FunctionEndpointInitializer,\ -org.springframework.cloud.function.web.source.FunctionExporterInitializer diff --git a/spring-cloud-function-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-function-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 74c853d0b..000000000 --- a/spring-cloud-function-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,3 +0,0 @@ -org.springframework.cloud.function.web.flux.ReactorAutoConfiguration -org.springframework.cloud.function.web.mvc.ReactorAutoConfiguration -org.springframework.cloud.function.web.source.FunctionExporterAutoConfiguration diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/flux/FluxRestApplicationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/flux/FluxRestApplicationTests.java deleted file mode 100644 index 660910910..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/flux/FluxRestApplicationTests.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.flux; - -import java.net.URI; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.flux.FluxRestApplicationTests.TestConfiguration; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for vanilla MVC handling (no function layer). Validates the MVC customizations - * that are added in this project independently of the specific concerns of function. - * - * @author Dave Syer - * - */ -// @checkstyle:off -@SpringBootTest(classes = TestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive") -// @checkstyle:on -public class FluxRestApplicationTests { - - private static final MediaType EVENT_STREAM = MediaType.valueOf("text/event-stream"); - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Autowired - private TestConfiguration test; - - @BeforeEach - public void init() { - this.test.list.clear(); - } - - @Test - public void wordsSSE() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), - String.class).getBody()).isEqualTo(sse("foo", "bar")); - } - - @Test - public void wordsJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/words")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - @Disabled("Fix error handling") - public void errorJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/bang")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); - } - - @Test - public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void foos() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/foos")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); - } - - @Test - public void getMore() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/get/more")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - @Disabled("Should this even work? Or do we need to be explicit about the JSON?") - public void updates() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/updates")).body("one\ntwo"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isEqualTo("onetwo"); - } - - @Test - public void updatesJson() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isEqualTo("[\"one\",\"two\"]"); - } - - @Test - public void addFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); - } - - @Test - public void timeout() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/timeout")).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); - } - - //@Test - public void emptyJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/empty")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[]"); - } - - @Test - public void sentences() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/sentences")).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - } - - @Test - public void sentencesAcceptAny() throws Exception { - assertThat( - this.rest - .exchange(RequestEntity.get(new URI("/sentences")) - .accept(MediaType.ALL).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - } - - @Test - public void sentencesAcceptJson() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.get(new URI("/sentences")) - .accept(MediaType.APPLICATION_JSON).build(), - String.class); - assertThat(result.getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - assertThat(result.getHeaders().getContentType()) - .isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); - } - - @Test - public void uppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); - } - - @Test - public void uppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - public void transform() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/transform")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); - } - - @Test - public void postMore() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/post/more")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); - } - - @Test - public void uppercaseGet() throws Exception { - assertThat(this.rest.exchange(RequestEntity.get(new URI("/uppercase/foo")) - .accept(MediaType.TEXT_PLAIN).build(), String.class).getBody()) - .isEqualTo("[FOO]"); - } - - @Test - public void convertGet() throws Exception { - assertThat(this.rest.exchange(RequestEntity.get(new URI("/wrap/123")) - .accept(MediaType.TEXT_PLAIN).build(), String.class).getBody()) - .isEqualTo("..123.."); - } - - @Test - public void convertGetJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/entity/321")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("{\"value\":321}"); - } - - @Test - public void uppercaseJsonStream() throws Exception { - assertThat( - this.rest.exchange( - RequestEntity.post(new URI("/maps")) - .contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), - String.class).getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - public void uppercaseSSE() throws Exception { - assertThat(this.rest.exchange(RequestEntity.post(new URI("/uppercase")) - .accept(EVENT_STREAM).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("[FOO]", "[BAR]")); - } - - @Test - public void altSSE() throws Exception { - assertThat(this.rest.exchange(RequestEntity.post(new URI("/alt")) - .accept(EVENT_STREAM).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("[FOO]", "[BAR]")); - } - - private String sse(String... values) { - return "data:" + StringUtils.arrayToDelimitedString(values, "\n\ndata:") + "\n\n"; - } - - @EnableAutoConfiguration - @RestController - @Configuration - public static class TestConfiguration { - - private List list = new ArrayList<>(); - - @PostMapping({ "/uppercase", "/transform", "/post/more" }) - public Flux uppercase(@RequestBody List flux) { - return Flux.fromIterable(flux).log() - .map(value -> "[" + value.trim().toUpperCase() + "]"); - } - - @PostMapping({ "/alt" }) - public Mono> alt(@RequestBody List flux) { - Publisher result = Flux.fromIterable(flux) - .map(value -> "[" + value.trim().toUpperCase() + "]"); - return Flux.from(result).log() - .then(Mono.fromSupplier(() -> ResponseEntity.ok(result))); - } - - @PostMapping("/upFoos") - public Flux upFoos(@RequestBody List list) { - return Flux.fromIterable(list).log() - .map(value -> new Foo(value.getValue().trim().toUpperCase())); - } - - @GetMapping("/uppercase/{id}") - public Mono> uppercaseGet(@PathVariable String id) { - return Mono.just(id).map(value -> "[" + value.trim().toUpperCase() + "]") - .flatMap(body -> Mono.just(ResponseEntity.ok(body))); - } - - @GetMapping("/wrap/{id}") - public Mono> wrapGet(@PathVariable int id) { - return Mono.just(id).log().map(value -> ".." + value + "..") - .flatMap(body -> Mono.just(ResponseEntity.ok(body))); - } - - @GetMapping("/entity/{id}") - public Mono> entity(@PathVariable Integer id) { - return Mono.just(id).log() - .map(value -> Collections.singletonMap("value", value)); - } - - @PostMapping("/maps") - public Flux> maps( - @RequestBody List> flux) { - return Flux.fromIterable(flux).map(value -> { - value.put("value", value.get("value").trim().toUpperCase()); - return value; - }); - } - - @GetMapping({ "/words", "/get/more" }) - public Flux words() { - return Flux.fromArray(new String[] { "foo", "bar" }); - } - - @GetMapping("/foos") - public Flux foos() { - return Flux.just(new Foo("foo"), new Foo("bar")); - } - - @PostMapping("/updates") - @ResponseStatus(HttpStatus.ACCEPTED) - public Flux updates(@RequestBody List list) { - Flux flux = Flux.fromIterable(list).cache(); - flux.subscribe(value -> this.list.add(value)); - return flux; - } - - @PostMapping("/addFoos") - @ResponseStatus(HttpStatus.ACCEPTED) - public Flux addFoos(@RequestBody List list) { - Flux flux = Flux.fromIterable(list).cache(); - flux.subscribe(value -> this.list.add(value.getValue())); - return flux; - } - - @GetMapping("/bang") - public Flux bang() { - return Flux.fromArray(new String[] { "foo", "bar" }).map(value -> { - if (value.equals("bar")) { - throw new RuntimeException("Bar"); - } - return value; - }); - } - - @GetMapping("/empty") - public Flux empty() { - return Flux.fromIterable(Collections.emptyList()); - } - - @GetMapping("/timeout") - public Flux timeout() { - return Flux.defer(() -> Flux.create(emitter -> { - emitter.next("foo"); - }).timeout(Duration.ofMillis(100L), Flux.empty())); - } - - @GetMapping("/sentences") - public Flux> sentences() { - return Flux.just(Arrays.asList("go", "home"), Arrays.asList("come", "back")); - } - - } - - public static class Foo { - - private String value; - - public Foo(String value) { - this.value = value; - } - - Foo() { - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/mvc/MvcRestApplicationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/mvc/MvcRestApplicationTests.java deleted file mode 100644 index 1d051ae3b..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/mvc/MvcRestApplicationTests.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.mvc; - -import java.net.URI; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.mvc.MvcRestApplicationTests.TestConfiguration; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for vanilla MVC handling (no function layer). Validates the MVC customizations - * that are added in this project independently of the specific concerns of function. - * - * @author Dave Syer - * - */ -// @checkstyle:off -@SpringBootTest(classes = TestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=servlet") -// @checkstyle:on -public class MvcRestApplicationTests { - - private static final MediaType EVENT_STREAM = MediaType.valueOf("text/event-stream"); - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Autowired - private TestConfiguration test; - - @BeforeEach - public void init() { - this.test.list.clear(); - } - - @Test - public void wordsSSE() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), - String.class).getBody()).isEqualTo(sse("foo", "bar")); - } - - @Test - public void wordsJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/words")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - @Disabled("Fix error handling") - public void errorJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/bang")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); - } - - @Test - public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void foos() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/foos")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); - } - - @Test - public void getMore() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/get/more")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - @Disabled("Should this even work? Or do we need to be explicit about the JSON?") - public void updates() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/updates")).body("one\ntwo"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isEqualTo("onetwo"); - } - - @Test - public void updatesJson() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isEqualTo("[\"one\",\"two\"]"); - } - - @Test - public void addFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); - } - - @Test - public void timeout() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/timeout")).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); - } - - @Test - public void emptyJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/empty")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[]"); - } - - @Test - public void sentences() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/sentences")).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - } - - @Test - public void sentencesAcceptAny() throws Exception { - assertThat( - this.rest - .exchange(RequestEntity.get(new URI("/sentences")) - .accept(MediaType.ALL).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - } - - @Test - public void sentencesAcceptJson() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.get(new URI("/sentences")) - .accept(MediaType.APPLICATION_JSON).build(), - String.class); - assertThat(result.getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - assertThat(result.getHeaders().getContentType()) - .isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); - } - - @Test - public void uppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); - } - - @Test - public void uppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - public void transform() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/transform")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); - } - - @Test - public void postMore() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/post/more")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"[FOO]\",\"[BAR]\"]"); - } - - @Test - public void uppercaseGet() { - assertThat(this.rest.getForObject("/uppercase/foo", String.class)) - .isEqualTo("[FOO]"); - } - - @Test - public void convertGet() { - assertThat(this.rest.getForObject("/wrap/123", String.class)) - .isEqualTo("..123.."); - } - - @Test - public void convertGetJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/entity/321")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("{\"value\":321}"); - } - - @Test - public void uppercaseJsonStream() throws Exception { - assertThat( - this.rest.exchange( - RequestEntity.post(new URI("/maps")) - .contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), - String.class).getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - public void uppercaseSSE() throws Exception { - assertThat(this.rest.exchange(RequestEntity.post(new URI("/uppercase")) - .accept(EVENT_STREAM).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("[FOO]", "[BAR]")); - } - - private String sse(String... values) { - return "data:" + StringUtils.arrayToDelimitedString(values, "\n\ndata:") + "\n\n"; - } - - @EnableAutoConfiguration - @RestController - @Configuration - public static class TestConfiguration { - - private List list = new ArrayList<>(); - - @PostMapping({ "/uppercase", "/transform", "/post/more" }) - public Flux uppercase(@RequestBody List flux) { - return Flux.fromIterable(flux).log() - .map(value -> "[" + value.trim().toUpperCase() + "]"); - } - - @PostMapping("/upFoos") - public Flux upFoos(@RequestBody List list) { - return Flux.fromIterable(list).log() - .map(value -> new Foo(value.getValue().trim().toUpperCase())); - } - - @GetMapping("/uppercase/{id}") - public Mono uppercaseGet(@PathVariable String id) { - return Mono.just(id).map(value -> "[" + value.trim().toUpperCase() + "]"); - } - - @GetMapping("/wrap/{id}") - public Mono wrapGet(@PathVariable int id) { - return Mono.just(id).log().map(value -> ".." + value + ".."); - } - - @GetMapping("/entity/{id}") - public Mono> entity(@PathVariable Integer id) { - return Mono.just(id).log() - .map(value -> Collections.singletonMap("value", value)); - } - - @PostMapping("/maps") - public Flux> maps( - @RequestBody List> flux) { - return Flux.fromIterable(flux).map(value -> { - value.put("value", value.get("value").trim().toUpperCase()); - return value; - }); - } - - @GetMapping({ "/words", "/get/more" }) - public Flux words() { - return Flux.fromArray(new String[] { "foo", "bar" }); - } - - @GetMapping("/foos") - public Flux foos() { - return Flux.just(new Foo("foo"), new Foo("bar")); - } - - @PostMapping("/updates") - @ResponseStatus(HttpStatus.ACCEPTED) - public Flux updates(@RequestBody List list) { - Flux flux = Flux.fromIterable(list).cache(); - flux.subscribe(value -> this.list.add(value)); - return flux; - } - - @PostMapping("/addFoos") - @ResponseStatus(HttpStatus.ACCEPTED) - public Flux addFoos(@RequestBody List list) { - Flux flux = Flux.fromIterable(list).cache(); - flux.subscribe(value -> this.list.add(value.getValue())); - return flux; - } - - @GetMapping("/bang") - public Flux bang() { - return Flux.fromArray(new String[] { "foo", "bar" }).map(value -> { - if (value.equals("bar")) { - throw new RuntimeException("Bar"); - } - return value; - }); - } - - @GetMapping("/empty") - public Flux empty() { - return Flux.fromIterable(Collections.emptyList()); - } - - @GetMapping("/timeout") - public Flux timeout() { - return Flux.defer(() -> Flux.create(emitter -> { - emitter.next("foo"); - }).timeout(Duration.ofMillis(100L), Flux.empty())); - } - - @GetMapping("/sentences") - public Flux> sentences() { - return Flux.just(Arrays.asList("go", "home"), Arrays.asList("come", "back")); - } - - } - - public static class Foo { - - private String value; - - public Foo(String value) { - this.value = value; - } - - Foo() { - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/scan/ComponentTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/scan/ComponentTests.java deleted file mode 100644 index 9c9cfc64f..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/scan/ComponentTests.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.scan; - -import java.net.URI; -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -public class ComponentTests { - - @LocalServerPort - private int port; - - @Autowired - private Greeter greeter; - - @Autowired - private TestRestTemplate rest; - - @Test - public void contextLoads() throws Exception { - assertThat(this.greeter).isNotNull(); - } - - @Test - public void greeter() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/greeter")) - .contentType(MediaType.TEXT_PLAIN).body("World"), - String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"Hello World\"]"); - } - - @SpringBootApplication - @ComponentScan - protected static class TestConfiguration { - - } - - @Component("greeter") - protected static class Greeter implements Function, Flux> { - - @Override - public Flux apply(Flux flux) { - return flux.map(name -> "Hello " + name); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ExplicitNonFunctionalTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ExplicitNonFunctionalTests.java deleted file mode 100644 index a25046754..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ExplicitNonFunctionalTests.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Dave Syer - * - */ -@SpringBootTest({ "spring.main.web-application-type=REACTIVE", - "spring.functional.enabled=false" }) -@AutoConfigureWebTestClient -@DirtiesContext -public class ExplicitNonFunctionalTests { - - @Autowired - private WebTestClient client; - - @Test - public void words() throws Exception { - this.client.post().uri("/").body(Mono.just("foo"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("FOO"); - } - - @SpringBootConfiguration - @EnableAutoConfiguration - protected static class TestConfiguration implements Function { - - @Override - public String apply(String value) { - return value.toUpperCase(); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java deleted file mode 100644 index 0f0826e83..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; -import org.springframework.cloud.function.test.FunctionalExporterTests.ApplicationConfiguration; -import org.springframework.cloud.function.web.TestSocketUtils; -import org.springframework.cloud.function.web.source.SupplierExporter; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - */ -@FunctionalSpringBootTest(classes = ApplicationConfiguration.class, webEnvironment = WebEnvironment.NONE, properties = { - "spring.main.web-application-type=none", - "spring.cloud.function.web.export.sink.url=http://localhost:${my.port}", - "spring.cloud.function.web.export.source.url=http://localhost:${my.port}", - "spring.cloud.function.web.export.sink.name=origin|uppercase", - "spring.cloud.function.web.export.sink.contentType=text/plain", - "spring.cloud.function.web.export.debug=true" }) -@Disabled -public class FunctionalExporterTests { - - @Autowired - private SupplierExporter forwarder; - - private static RestPojoConfiguration app; - - private static ConfigurableApplicationContext context; - - private static Map headers = new HashMap<>(); - - @BeforeAll - public static void init() throws Exception { - headers.clear(); - String port = "" + TestSocketUtils.findAvailableTcpPort(); - System.setProperty("server.port", port); - System.setProperty("my.port", port); - context = SpringApplication.run(RestPojoConfiguration.class, - "--spring.main.web-application-type=reactive"); - app = context.getBean(RestPojoConfiguration.class); - // Sometimes the server doesn't start quick enough - Thread.sleep(500L); - } - - @AfterAll - public static void close() { - headers.clear(); - System.clearProperty("server.port"); - if (context != null) { - context.close(); - } - } - - @Test - public void words() throws Exception { - int count = 0; - while (this.forwarder.isRunning() && count++ < 10) { - Thread.sleep(50); - } - // It completed - assertThat(FunctionalExporterTests.app.inputs).contains("HELLO"); - assertThat(this.forwarder.isOk()).isTrue(); - assertThat(headers.containsKey("scf-sink-url")); - assertThat(headers.containsKey("scf-func-name")); - } - - @SpringBootConfiguration - protected static class ApplicationConfiguration - implements ApplicationContextInitializer { - - Function, Message> uppercase() { - return value -> { - headers.putAll(value.getHeaders()); - return MessageBuilder.withPayload(value.getPayload().getName().toUpperCase()) - .copyHeaders(value.getHeaders()).build(); - }; - } - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(uppercase()) - .type(FunctionTypeUtils.discoverFunctionTypeFromFunctionFactoryMethod(this.getClass(), "uppercase"))); - } - - public static Type discoverFunctionTypeFromFunctionFactoryMethod(Class clazz, String methodName) { - return discoverFunctionTypeFromFunctionFactoryMethod(ReflectionUtils.findMethod(clazz, methodName)); - } - - public static Type discoverFunctionTypeFromFunctionFactoryMethod(Method method) { - return method.getGenericReturnType(); - } - } - -} - -class Person { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalTests.java deleted file mode 100644 index 00883f4c8..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalTests.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Dave Syer - * - */ -// Only need web-application-type because MVC is on the classpath -@FunctionalSpringBootTest("spring.main.web-application-type=reactive") -@AutoConfigureWebTestClient -public class FunctionalTests { - - @Autowired - private WebTestClient client; - - @Test - public void words() throws Exception { - this.client.post().uri("/").body(Mono.just("foo"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("FOO"); - } - - @SpringBootConfiguration - protected static class TestConfiguration implements Function { - - @Override - public String apply(String value) { - return value.toUpperCase(); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputListTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputListTests.java deleted file mode 100644 index d30b7df71..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputListTests.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Dave Syer - * - */ -@FunctionalSpringBootTest("spring.main.web-application-type=reactive") -@AutoConfigureWebTestClient -public class FunctionalWithInputListTests { - - @Autowired - private WebTestClient client; - - @Test - public void words() throws Exception { - this.client.post().uri("/") - .body(Mono.just("[{\"value\":\"foo\"}, {\"value\":\"bar\"}]"), - String.class) - .exchange().expectStatus().isOk().expectBody(String.class) - .isEqualTo("{\"value\":\"FOOBAR\"}"); - } - - @SpringBootConfiguration - protected static class TestConfiguration implements Function, Foo> { - - @Override - public Foo apply(List value) { - return new Foo(value.stream().map(foo -> foo.getValue().toUpperCase()) - .collect(Collectors.joining())); - } - - } - - public static class Foo { - - private String value; - - public Foo() { - } - - public Foo(String value) { - this.value = value; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - return "Foo [value=" + this.value + "]"; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputSetTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputSetTests.java deleted file mode 100644 index 066959957..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputSetTests.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.time.Duration; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@FunctionalSpringBootTest("spring.main.web-application-type=reactive") -@AutoConfigureWebTestClient -public class FunctionalWithInputSetTests { - - @Autowired - private WebTestClient client; - - @Test - public void words() throws Exception { - this.client = this.client.mutate().responseTimeout(Duration.ofSeconds(300)).build(); - String reply = this.client - .post().uri("/") - .body(Mono.just("[{\"value\":\"foo\"}, {\"value\":\"bar\"}]"), String.class) - .exchange() - .expectStatus().isOk().expectBody(String.class).returnResult() - .getResponseBody(); - assertThat(reply.contains("FOO")).isTrue(); - assertThat(reply.contains("BAR")).isTrue(); - assertThat(reply.contains("{\"value\":\"")).isTrue(); - } - - @SpringBootConfiguration - protected static class TestConfiguration implements Function, Foo> { - - @Override - public Foo apply(Set value) { - return new Foo(value.stream().map(foo -> foo.getValue().toUpperCase()) - .collect(Collectors.joining())); - } - - } - - public static class Foo { - - private String value; - - public Foo() { - } - - public Foo(String value) { - this.value = value; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - return "Foo [value=" + this.value + "]"; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/HeadersToMessageTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/HeadersToMessageTests.java deleted file mode 100644 index 710843883..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/HeadersToMessageTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.test.web.reactive.server.WebTestClient; - - -/** - * @author Oleg Zhurakousky - * - */ -// Only need web-application-type because MVC is on the classpath -@FunctionalSpringBootTest("spring.main.web-application-type=reactive") -@AutoConfigureWebTestClient -public class HeadersToMessageTests { - - @Autowired - private WebTestClient client; - - @Test - public void testBodyAndCustomHeaderFromMessagePropagation() throws Exception { - this.client.post().uri("/").body(Mono.just("foo"), String.class).exchange() - .expectStatus().isOk().expectHeader() - .valueEquals("x-content-type", "application/xml").expectHeader() - .valueEquals("foo", "bar").expectBody(String.class).isEqualTo("FOO"); - } - - @SpringBootConfiguration - protected static class TestConfiguration - implements Function, Message> { - - @Override - public Message apply(Message request) { - Message message = MessageBuilder - .withPayload(request.getPayload().toUpperCase()) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); - return message; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ImplicitNonFunctionalTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ImplicitNonFunctionalTests.java deleted file mode 100644 index 28e067430..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ImplicitNonFunctionalTests.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Dave Syer - * - */ -@SpringBootTest("spring.main.web-application-type=REACTIVE") -@AutoConfigureWebTestClient -@DirtiesContext -public class ImplicitNonFunctionalTests { - - @Autowired - private WebTestClient client; - - @Test - public void words() throws Exception { - this.client.post().uri("/").body(Mono.just("foo"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("FOO"); - } - - @SpringBootConfiguration - @EnableAutoConfiguration - protected static class TestConfiguration { - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/MoreThenOneFunctionRootMappingTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/MoreThenOneFunctionRootMappingTests.java deleted file mode 100644 index 1482024c5..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/MoreThenOneFunctionRootMappingTests.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Oleg Zhurakousky - * - */ -@SpringBootTest({ "spring.main.web-application-type=REACTIVE", - "spring.functional.enabled=false", - "spring.cloud.function.definition=uppercase|reverse" }) -@AutoConfigureWebTestClient -@DirtiesContext -public class MoreThenOneFunctionRootMappingTests { - - @Autowired - private WebTestClient client; - - @Test - public void words() { - this.client.post().uri("/").body(Mono.just("star"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("RATS"); - } - - @SpringBootConfiguration - @EnableAutoConfiguration - protected static class TestConfiguration { - - @Bean - public Function uppercase() { - return String::toUpperCase; - } - - @Bean - public Function reverse() { - return v -> new StringBuilder(v).reverse().toString(); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/PojoTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/PojoTests.java deleted file mode 100644 index b622e8a0f..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/PojoTests.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Dave Syer - * - */ -// Only need web-application-type because MVC is on the classpath -@FunctionalSpringBootTest("spring.main.web-application-type=reactive") -@AutoConfigureWebTestClient -public class PojoTests { - - @Autowired - private WebTestClient client; - - @Test - public void single() throws Exception { - this.client.post().uri("/").body(Mono.just("{\"value\":\"foo\"}"), String.class) - .exchange().expectStatus().isOk().expectBody(String.class) - .isEqualTo("{\"value\":\"FOO\"}"); - } - - @Test - public void multiple() throws Exception { - this.client.post().uri("/") - .body(Mono.just("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), - String.class) - .exchange().expectStatus().isOk().expectBody(String.class) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @SpringBootConfiguration - protected static class TestConfiguration implements Function { - - @Override - public Foo apply(Foo value) { - return new Foo(value.getValue().toUpperCase()); - } - - } - -} - -class Foo { - - private String value; - - Foo() { - } - - Foo(String value) { - this.value = value; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - return "Foo [value=" + this.value + "]"; - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/RestConfiguration.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/RestConfiguration.java deleted file mode 100644 index 39bb8fae7..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/RestConfiguration.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2018-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -@SpringBootConfiguration -@EnableAutoConfiguration -@RestController -public class RestConfiguration { - - private static Log logger = LogFactory.getLog(RestConfiguration.class); - - List inputs = new ArrayList<>(); - - private Iterator outputs = Arrays.asList("hello", "world").iterator(); - - @GetMapping("/") - ResponseEntity home() { - logger.info("HOME"); - if (this.outputs.hasNext()) { - return ResponseEntity.ok(this.outputs.next()); - } - return ResponseEntity.notFound().build(); - } - - @PostMapping("/") - ResponseEntity accept(@RequestBody String body) { - logger.info("ACCEPT"); - this.inputs.add(body); - return ResponseEntity.accepted().body(body); - } - - public static void main(String[] args) throws Exception { - SpringApplication.run(RestConfiguration.class, args); - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/RestPojoConfiguration.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/RestPojoConfiguration.java deleted file mode 100644 index fbb89d14f..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/RestPojoConfiguration.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2018-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -@SpringBootConfiguration -@EnableAutoConfiguration -@RestController -public class RestPojoConfiguration { - - private static Log logger = LogFactory.getLog(RestPojoConfiguration.class); - - List inputs = new ArrayList<>(); - - private Iterator outputs = Arrays.asList("{\"name\":\"hello\"}").iterator(); - - @GetMapping("/") - ResponseEntity home() { - logger.info("HOME"); - if (this.outputs.hasNext()) { - return ResponseEntity.ok(this.outputs.next()); - } - return ResponseEntity.notFound().build(); - } - - @PostMapping("/") - ResponseEntity accept(@RequestBody String body) { - logger.info("ACCEPT"); - this.inputs.add(body); - return ResponseEntity.accepted().body(body); - } - - public static void main(String[] args) throws Exception { - SpringApplication.run(RestPojoConfiguration.class, args); - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/TestSocketUtils.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/TestSocketUtils.java deleted file mode 100644 index 5c35613de..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/TestSocketUtils.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web; - -import java.net.InetAddress; -import java.net.ServerSocket; -import java.util.Random; - -import javax.net.ServerSocketFactory; - -/** - * Simple test utility to find a random available TCP port. - *

Inspired by the now removed {@code org.springframework.util.SocketUtils} and is only used in a testing capacity. - * - * @author Chris Bono - * @deprecated will soon be removed or consolidated - do not use further - */ -@Deprecated -public final class TestSocketUtils { - - private static final Random random = new Random(System.nanoTime()); - - private TestSocketUtils() { - } - - /** - * Find an available TCP port randomly selected from the range {@code 1024-65535}. - * @return an available TCP port number - * @throws IllegalStateException if no available port could be found - */ - public static int findAvailableTcpPort() { - int minPort = 1024; - int maxPort = 65535; - int portRange = maxPort - minPort; - int candidatePort; - int searchCounter = 0; - do { - if (searchCounter > portRange) { - throw new IllegalStateException(String.format( - "Could not find an available TCP port after %d attempts", searchCounter)); - } - candidatePort = minPort + random.nextInt(portRange + 1); - searchCounter++; - } - while (!isPortAvailable(candidatePort)); - - return candidatePort; - } - - private static boolean isPortAvailable(int port) { - try { - ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket( - port, 1, InetAddress.getByName("localhost")); - serverSocket.close(); - return true; - } - catch (Exception ex) { - return false; - } - } -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HeadersToMessageTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HeadersToMessageTests.java deleted file mode 100644 index 06ec2bdb1..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HeadersToMessageTests.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.flux; - -import java.net.URI; -import java.util.function.Function; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - * @author Adrien Poupard - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.cloud.function.web.path=/functions", - "spring.main.web-application-type=reactive" }) -@ContextConfiguration(classes = { RestApplication.class, HeadersToMessageTests.TestConfiguration.class }) -public class HeadersToMessageTests { - - @Autowired - private TestRestTemplate rest; - - @Test - public void testBodyAndCustomHeaderFromMessagePropagation() throws Exception { - // test POJO paylod - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/employee")) - .contentType(MediaType.APPLICATION_JSON) - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); - assertThat(postForEntity.getBody()).isEqualTo("{\"name\":\"Bob\",\"age\":25}"); - assertThat(postForEntity.getHeaders().containsKey("x-content-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-content-type").get(0)) - .isEqualTo("application/xml"); - assertThat(postForEntity.getHeaders().get("foo").get(0)).isEqualTo("bar"); - - // test simple type payload - postForEntity = this.rest.postForEntity(new URI("/functions/string"), - "{\"name\":\"Bob\",\"age\":25}", String.class); - assertThat(postForEntity.getBody()).isEqualTo("{\"name\":\"Bob\",\"age\":25}"); - assertThat(postForEntity.getHeaders().containsKey("x-content-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-content-type").get(0)) - .isEqualTo("application/xml"); - assertThat(postForEntity.getHeaders().get("foo").get(0)).isEqualTo("bar"); - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - protected static class TestConfiguration { - - @Bean({ "string" }) - public Function, Message> functiono() { - return request -> { - Message message = MessageBuilder.withPayload(request.getPayload()) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); - return message; - }; - } - - @Bean({ "employee" }) - public Function, Message> function1() { - return request -> { - Message message = MessageBuilder - .withPayload(request.getPayload()) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); - return message; - }; - } - - } - - @SuppressWarnings("unused") // used by json converter - private static class Employee { - - private String name; - - private int age; - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public int getAge() { - return this.age; - } - - public void setAge(int age) { - this.age = age; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpGetIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpGetIntegrationTests.java deleted file mode 100644 index 2505c6a53..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpGetIntegrationTests.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.flux; - -import java.net.URI; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.flux.HttpGetIntegrationTests.ApplicationConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"spring.main.web-application-type=reactive", "debug=true"}) -@ContextConfiguration(classes = { RestApplication.class, ApplicationConfiguration.class }) -@DirtiesContext -public class HttpGetIntegrationTests { - - private static final MediaType EVENT_STREAM = MediaType.TEXT_EVENT_STREAM; - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Autowired - private ApplicationConfiguration test; - - @BeforeEach - public void init() { - this.test.list.clear(); - } - - @Test - public void staticResource() { - assertThat(this.rest.getForObject("/test.html", String.class)) - .contains("Test"); - } - - @Test - public void wordsSSE() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), - String.class).getBody()).isEqualTo(sse("foo", "bar")); - } - - @Test - public void wordsJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/words")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void errorJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/bang")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); - } - - @Test - public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void word() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/word")).accept(MediaType.TEXT_PLAIN).build(), - String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("foo"); - } - - @Test - public void foos() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/foos")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); - } - - @Test - public void getMore() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/get/more")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void bareWords() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/bareWords")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void timeoutJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/timeout")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); - } - - @Test - public void emptyJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/empty")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[]"); - } - - @Test - public void sentences() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/sentences")).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - } - - @Test - public void sentencesAcceptAny() throws Exception { - assertThat( - this.rest - .exchange(RequestEntity.get(new URI("/sentences")) - .accept(MediaType.ALL).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - } - - @Test - public void sentencesAcceptJson() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.get(new URI("/sentences")) - .accept(MediaType.APPLICATION_JSON).build(), - String.class); - assertThat(result.getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - assertThat(result.getHeaders().getContentType()) - .isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); - } - - @Test - public void sentencesAcceptSse() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/sentences")).accept(EVENT_STREAM).build(), - String.class); - assertThat(result.getBody()) - .isEqualTo(sse("[\"go\",\"home\"]", "[\"come\",\"back\"]")); - assertThat(result.getHeaders().getContentType().isCompatibleWith(EVENT_STREAM)) - .isTrue(); - } - - @Test - public void postMoreFoo() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/post/more/foo")).accept(MediaType.TEXT_PLAIN).build(), - String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\"]"); - } - - @Test - public void uppercaseGet() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/uppercase/foo")).accept(MediaType.TEXT_PLAIN).build(), - String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\"]"); - } - - @Test - public void convertGet() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/wrap/123")).accept(MediaType.TEXT_PLAIN).build(), - String.class); - assertThat(result.getBody()).isEqualTo("[\"..123..\"]"); - } - - @Test - public void supplierFirst() { - assertThat(this.rest.getForObject("/not/a/function", String.class)) - .isEqualTo("[\"hello\"]"); - } - - @Test - public void convertGetJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/entity/321")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[{\"value\":321}]"); - } - - @Test - public void compose() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/concat,reverse/foo")).accept(MediaType.TEXT_PLAIN).build(), - String.class); - assertThat(result.getBody()).isEqualTo("[\"oofoof\"]"); - } - - private String sse(String... values) { - return "data:" + StringUtils.arrayToDelimitedString(values, "\n\ndata:") + "\n\n"; - } - - @EnableAutoConfiguration - @TestConfiguration - public static class ApplicationConfiguration { - - private List list = new ArrayList<>(); - - public static void main(String[] args) throws Exception { - SpringApplication.run(HttpGetIntegrationTests.ApplicationConfiguration.class, - args); - } - - @Bean - public Function, Flux> concat() { - return flux -> flux.map(v -> v + v); - } - - @Bean - public Function, Flux> reverse() { - return flux -> flux.log() - .map(value -> new StringBuilder(value.trim()).reverse().toString()); - } - - @Bean({ "uppercase", "post/more" }) - public Function, Flux> uppercase() { - return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase() + ")"); - } - - @Bean - public Function, Flux> wrap() { - return flux -> flux.log().map(value -> ".." + value + ".."); - } - - @Bean - public Function, Flux>> entity() { - return flux -> flux.log() - .map(value -> Collections.singletonMap("value", value)); - } - - @Bean({ "words", "get/more" }) - public Supplier> words() { - return () -> Flux.just("foo", "bar"); - } - - @Bean - public Supplier word() { - return () -> "foo"; - } - - @Bean - public Supplier> foos() { - return () -> Flux.just(new Foo("foo"), new Foo("bar")); - } - - @Bean - public Supplier> bareWords() { - return () -> Arrays.asList("foo", "bar"); - } - - @Bean - public Supplier> bang() { - return () -> Flux.fromArray(new String[] { "foo", "bar" }).map(value -> { - if (value.equals("bar")) { - throw new RuntimeException("Bar"); - } - return value; - }); - } - - @Bean - public Supplier> empty() { - return () -> Flux.fromIterable(Collections.emptyList()); - } - - @Bean("not/a/function") - public Supplier> supplier() { - return () -> Flux.just("hello"); - } - - @Bean("not/a") - public Function, Flux> function() { - return input -> Flux.just("bye"); - } - - @Bean - public Supplier> timeout() { - return () -> Flux.defer(() -> Flux.create(emitter -> { - emitter.next("foo"); - }).timeout(Duration.ofMillis(100L), Flux.empty())); - } - - @Bean - public Supplier>> sentences() { - return () -> Flux.just(Arrays.asList("go", "home"), - Arrays.asList("come", "back")); - } - - @Bean - public Function, Map> sum() { - return valueMap -> valueMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, values -> values - .getValue().stream().mapToInt(Integer::parseInt).sum())); - } - - } - - public static class Foo { - - private String value; - - public Foo(String value) { - this.value = value; - } - - Foo() { - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpPostIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpPostIntegrationTests.java deleted file mode 100644 index 0598c06f7..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpPostIntegrationTests.java +++ /dev/null @@ -1,553 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.flux; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.flux.HttpPostIntegrationTests.ApplicationConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.util.Assert; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive") -@ContextConfiguration(classes = { RestApplication.class, ApplicationConfiguration.class }) -public class HttpPostIntegrationTests { - - private static final MediaType EVENT_STREAM = MediaType.TEXT_EVENT_STREAM; - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Autowired - private ApplicationConfiguration test; - - @BeforeEach - public void init() { - this.test.list.clear(); - } - - @AfterEach - public void done() { - this.test.list.clear(); - } - - @Test - @DirtiesContext - public void qualifierFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/foos")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"[FOO]\"},{\"value\":\"[BAR]\"}]"); - } - - @Test - @DirtiesContext - public void updates() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\", \"two\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isNull(); - } - - @Test - @DirtiesContext - public void updatesJson() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isEqualTo(null); - } - - @Test - @DirtiesContext - public void addFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isEqualTo(null); - } - - @Test - @DirtiesContext - public void addFoosFlux() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoosFlux")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isEqualTo(null); - } - - @Test - @DirtiesContext - public void bareUpdates() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpdates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - } - - @Test - @DirtiesContext - public void uppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - @DirtiesContext - public void messages() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/messages")).contentType(MediaType.APPLICATION_JSON) - .header("x-foo", "bar").body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getHeaders().getFirst("x-foo")).isEqualTo("bar"); - assertThat(result.getHeaders()).doesNotContainKey("id"); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - @DirtiesContext - public void headers() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/headers")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); -// assertThat(result.getHeaders().getFirst("foo")).isEqualTo("bar"); -// assertThat(result.getHeaders()).doesNotContainKey("id"); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - @DirtiesContext - public void uppercaseSingleValue() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/uppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo"), - String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\"]"); - } - - @Test - @Disabled("WebFlux would split the request body into lines: TODO make this work the same") - public void uppercasePlainText() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/uppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo\nbar"), - String.class); - assertThat(result.getBody()).isEqualTo("(FOO\nBAR)"); - } - - @Test - @DirtiesContext - public void uppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - @DirtiesContext - public void uppercaseFoo() throws Exception { - // Single Foo can be parsed - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("{\"value\":\"foo\"}"), String.class); - assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"}]"); - } - - @Test - @DirtiesContext - public void bareUppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - @DirtiesContext - @Disabled // not sure if this test is correct. Why does ? has to be assumed as String? - public void typelessFunctionPassingArray() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/typelessFunctionExpectingText")) - .contentType(MediaType.TEXT_PLAIN).body("[{\"value\":\"foo\"}]"), - String.class); - assertThat(result.getBody()).isEqualTo("[{\"value\":\"foo\"}]"); - } - - @Test - @DirtiesContext - public void bareUppercaseFoo() throws Exception { - // Single Foo can be parsed and returns a single value if the function is defined - // that way - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpFoos")).contentType(MediaType.APPLICATION_JSON) - .body("{\"value\":\"foo\"}"), String.class); - assertThat(result.getBody()).isEqualTo("{\"value\":\"FOO\"}"); - } - - @Test - @DirtiesContext - public void bareUppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - @DirtiesContext - public void singleValuedText() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/bareUppercase")).accept(MediaType.TEXT_PLAIN) - .contentType(MediaType.TEXT_PLAIN).body("foo"), - String.class); - assertThat(result.getBody()).isEqualTo("(FOO)"); - } - - @Test - @DirtiesContext - public void transform() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/transform")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - @DirtiesContext - public void postMore() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/post/more")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - @DirtiesContext - public void convertPost() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/wrap")) - .contentType(MediaType.TEXT_PLAIN).body("123"), - String.class); - // Result is multi-valued so it has to come out as an array - assertThat(result.getBody()).isEqualTo("[\"..123..\"]"); - } - - @Test - @DirtiesContext - public void convertPostJson() throws Exception { - // If you POST a single value to a Function,Flux> it can't - // determine if the output is single valued, so it has to send an array back - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/doubler")) - .contentType(MediaType.TEXT_PLAIN).body("123"), - String.class); - assertThat(result.getBody()).isEqualTo("[246]"); - } - - @Test - @DirtiesContext - public void uppercaseJsonArray() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/maps")) - .contentType(MediaType.APPLICATION_JSON) - // The new line in the middle is optional - .body("[{\"value\":\"foo\"},\n{\"value\":\"bar\"}]"), - String.class).getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - @DirtiesContext - public void uppercaseSSE() throws Exception { - String s = this.rest.exchange(RequestEntity.post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody(); - assertThat(this.rest.exchange(RequestEntity.post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("(FOO)", "(BAR)")); - } - - @Test - @DirtiesContext - public void sum() throws Exception { - - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - - map.put("A", Arrays.asList("1", "2", "3")); - map.put("B", Arrays.asList("5", "6")); - - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/sum")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_FORM_URLENCODED).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); - } - - @Test - @DirtiesContext - @Disabled - public void multipart() throws Exception { - - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - - map.put("A", Arrays.asList("1", "2", "3")); - map.put("B", Arrays.asList("5", "6")); - - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/sum")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); - } - - @Test - @DirtiesContext - @Disabled - public void count() throws Exception { - List list = Arrays.asList("A", "B", "A"); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/count")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON).body(list), - String.class).getBody()).isEqualTo("{\"A\":2,\"B\":1}"); - } - - @Test - @DirtiesContext - @Disabled - public void fluxWithList() throws Exception { - List list = Arrays.asList("A", "B", "A"); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/fluxCollectionEcho")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON).body(list), - String.class).getBody()).isEqualTo("[\"A\",\"B\",\"A\"]"); - } - - private String sse(String... values) { - return "[\"" + StringUtils.arrayToDelimitedString(values, "\",\"") + "\"]"; - } - - @EnableAutoConfiguration - public static class ApplicationConfiguration { - - private List list = new ArrayList<>(); - - public static void main(String[] args) throws Exception { - SpringApplication.run(HttpPostIntegrationTests.ApplicationConfiguration.class, - args); - } - - @Bean({ "uppercase", "transform", "post/more" }) - public Function, Flux> uppercase() { - return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase() + ")"); - } - - @Bean - public Function bareUppercase() { - return value -> "(" + value.trim().toUpperCase() + ")"; - } - - @Bean - public Function, Message> messages() { - return value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase() + ")") - .copyHeaders(value.getHeaders()).build(); - } - - @Bean - public Function>, Flux>> headers() { - return flux -> flux.map(value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase() + ")") - .setHeader("foo", "bar").build()); - } - - @Bean - public Function, Flux> upFoos() { - return flux -> flux.log() - .map(value -> new Foo(value.getValue().trim().toUpperCase())); - } - - @Bean - public Function bareUpFoos() { - return value -> new Foo(value.getValue().trim().toUpperCase()); - } - - @Bean - public Function typelessFunctionExpectingText() { - return value -> { - Assert.isInstanceOf(String.class, value); - return value; - }; - } - - @Bean - public Function, Flux> wrap() { - return flux -> flux.log().map(value -> ".." + value + ".."); - } - - @Bean - public Function, Flux> doubler() { - return flux -> flux.log().map(value -> 2 * value); - } - - @Bean - public Function>, Flux>> maps() { - return flux -> flux.map(value -> { - value.put("value", value.get("value").trim().toUpperCase()); - return value; - }); - } - - @Bean - @Qualifier("foos") - public Function qualifier() { - return value -> { - return new Foo("[" + value.trim().toUpperCase() + "]"); - }; - } - - @Bean - public Consumer> updates() { - return flux -> flux.subscribe(value -> { - System.out.println(); - this.list.add(value); - }); - } - - @Bean - public Consumer> addFoosFlux() { - return flux -> flux.subscribe(value -> this.list.add(value.getValue())); - } - - @Bean - public Consumer addFoos() { - return value -> { - this.list.add(value.getValue()); - }; - } - - @Bean - public Consumer bareUpdates() { - return value -> { - this.list.add(value); - }; - } - - @Bean("not/a") - public Function, Flux> function() { - return input -> Flux.just("bye"); - } - - @Bean - public Function, Map> sum() { - return valueMap -> valueMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, values -> values - .getValue().stream().mapToInt(Integer::parseInt).sum())); - } - - @Bean - public Function, Mono>> count() { - return flux -> flux.collect(HashMap::new, - (map, word) -> map.merge(word, 1, Integer::sum)); - } - - @Bean - public Function>, Flux> fluxCollectionEcho() { - return flux -> flux.flatMap(v -> Flux.fromIterable(v)); - } - - } - - public static class Foo { - - private String value; - - public Foo(String value) { - this.value = value; - } - - Foo() { - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/PrefixTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/PrefixTests.java deleted file mode 100644 index fcadd4683..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/PrefixTests.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.flux; - -import java.net.URI; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.flux.PrefixTests.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.main.web-application-type=reactive", - "spring.cloud.function.web.path=/functions", "debug" }) -@ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) -public class PrefixTests { - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Test - public void words() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/functions/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void missing() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - protected static class TestConfiguration { - - @Bean({ "words", "get/more" }) - public Supplier> words() { - return () -> Flux.fromArray(new String[] { "foo", "bar" }); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/SingletonTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/SingletonTests.java deleted file mode 100644 index c2c85f328..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/SingletonTests.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.flux; - -import java.net.URI; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.flux.SingletonTests.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive") -@ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) -public class SingletonTests { - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Test - public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - protected static class TestConfiguration { - - @Bean - public static BeanDefinitionRegistryPostProcessor processor() { - return new BeanDefinitionRegistryPostProcessor() { - - @Override - public void postProcessBeanFactory( - ConfigurableListableBeanFactory beanFactory) - throws BeansException { - } - - @Override - public void postProcessBeanDefinitionRegistry( - BeanDefinitionRegistry registry) throws BeansException { - // Simulates what happens when you add a compiled function - RootBeanDefinition beanDefinition = new RootBeanDefinition( - MySupplier.class); - registry.registerBeanDefinition("words", beanDefinition); - } - }; - } - - } - - public static class MySupplier implements Supplier> { - - @Override - public Flux get() { - return Flux.just("foo", "bar"); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerMVCTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerMVCTests.java deleted file mode 100644 index e6aa8efd4..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerMVCTests.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2019-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.function; - -import java.net.URI; -import java.util.function.Function; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.http.ResponseEntity; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * @author Chris Bono - * @since 2.1 - */ -public class FunctionEndpointInitializerMVCTests { - - @Test - public void testSingleFunctionMapping() throws Exception { - ConfigurableApplicationContext context = SpringApplication.run(ApplicationConfiguration.class, "--server.port=0"); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - String port = context.getEnvironment().getProperty("local.server.port"); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase"), "stressed", String.class); - assertThat(response.getBody()).isEqualTo("STRESSED"); - response = testRestTemplate.postForEntity(new URI("http://localhost:" + port + "/reverse"), "stressed", String.class); - assertThat(response.getBody()).isEqualTo("desserts"); - } - - @Test - public void testCompositionFunctionMapping() throws Exception { - ConfigurableApplicationContext context = SpringApplication.run(ApplicationConfiguration.class, "--server.port=0"); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - String port = context.getEnvironment().getProperty("local.server.port"); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase,lowercase,reverse"), "stressed", String.class); - assertThat(response.getBody()).isEqualTo("desserts"); - } - - - @SpringBootApplication - protected static class ApplicationConfiguration { - - @Bean - public Function uppercase() { - return s -> s.toUpperCase(); - } - - @Bean - public Function lowercase() { - return s -> s.toLowerCase(); - } - - @Bean - public Function reverse() { - return s -> new StringBuilder(s).reverse().toString(); - } - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java deleted file mode 100644 index 4429d0ce4..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2019-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.function; - -import java.net.URI; -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionalSpringApplication; -import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.ResolvableType; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.util.UriComponentsBuilder; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -/** -* -* @author Oleg Zhurakousky -* @author Chris Bono -* @since 2.1 -*/ -public class FunctionEndpointInitializerTests { - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testEmptyBodyRequestParameters() throws Exception { - int port = startServerAndWaitForPort(BeansConfiguration.class, false); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - Map params = new HashMap<>(); - params.put("fname", "Jim"); - params.put("lname", "Lahey"); - - HttpHeaders headers = new HttpHeaders(); - headers.set("Accept", "application/json"); - HttpEntity entity = new HttpEntity(headers); - - String urlTemplate = UriComponentsBuilder.fromHttpUrl("http://localhost:" + port + "/nullPayload") - .queryParam("fname", "Jim").queryParam("lname", "Lahey").encode().toUriString(); - - ResponseEntity response = testRestTemplate.exchange(urlTemplate, HttpMethod.GET, entity, String.class); - String res = response.getBody(); - assertThat(res).contains("Jim"); - assertThat(res).contains("Lahey"); - } - - @Test - public void testNonExistingFunction() throws Exception { - int port = startServerAndWaitForPort(ApplicationConfiguration.class); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/foo"), "stressed", String.class); - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); - } - - @Test - public void testConsumerMapping() throws Exception { - int port = startServerAndWaitForPort(ConsumerConfiguration.class); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase"), "stressed", String.class); - assertThat(response.getBody()).isNull(); - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - } - - @Test - public void testSingleFunctionMapping() throws Exception { - int port = startServerAndWaitForPort(ApplicationConfiguration.class); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase"), "stressed", String.class); - assertThat(response.getBody()).isEqualTo("STRESSED"); - response = testRestTemplate.postForEntity(new URI("http://localhost:" + port + "/reverse"), "stressed", String.class); - assertThat(response.getBody()).isEqualTo("desserts"); - } - - @Test - public void testCompositionFunctionMapping() throws Exception { - int port = startServerAndWaitForPort(ApplicationConfiguration.class); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/uppercase,lowercase,reverse"), "stressed", String.class); - assertThat(response.getBody()).isEqualTo("desserts"); - } - - @Test - public void testGetWithtFunction() throws Exception { - int port = startServerAndWaitForPort(ApplicationConfiguration.class); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - ResponseEntity response = testRestTemplate - .getForEntity(new URI("http://localhost:" + port + "/reverse/stressed"), String.class); - System.out.println(); - assertThat(response.getBody()).isEqualTo("desserts"); - } - - @Test - public void testGetWithtSupplier() throws Exception { - int port = startServerAndWaitForPort(ApplicationConfiguration.class); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - ResponseEntity response = testRestTemplate - .getForEntity(new URI("http://localhost:" + port + "/supplier"), String.class); - assertThat(response.getBody()).isEqualTo("Jim Lahey"); - } - - private int startServerAndWaitForPort(Class primaryAppConfig, boolean functional) throws InterruptedException { - ConfigurableApplicationContext context = functional - ? FunctionalSpringApplication.run(primaryAppConfig, "--server.port=0") - : SpringApplication.run(primaryAppConfig, "--server.port=0"); - await() - .pollDelay(Duration.ofMillis(500)) - .pollInterval(Duration.ofMillis(500)) - .atMost(Duration.ofSeconds(3)) - .untilAsserted(() -> { - String port = context.getEnvironment().getProperty("local.server.port"); - assertThat(port).as("Unable to get 'local.server.port' - server may not have started up").isNotEmpty(); - }); - return Integer.valueOf(context.getEnvironment().getProperty("local.server.port")); - } - - private int startServerAndWaitForPort(Class primaryAppConfig) throws InterruptedException { - return this.startServerAndWaitForPort(primaryAppConfig, true); - } - - @SpringBootConfiguration - protected static class ConsumerConfiguration - implements ApplicationContextInitializer { - - public Consumer consume() { - return v -> System.out.println(v); - } - - @Override - public void initialize(GenericApplicationContext applicationContext) { - - applicationContext.registerBean("consume", FunctionRegistration.class, - () -> new FunctionRegistration<>(consume()) - .type(ResolvableType.forClassWithGenerics(Consumer.class, String.class).getType())); - } - - } - - @EnableAutoConfiguration - @Configuration - protected static class BeansConfiguration { - @Bean - public BiFunction, Map> nullPayload() { - return (p, h) -> { - return h; - }; - } - } - - - @SpringBootConfiguration - protected static class ApplicationConfiguration - implements ApplicationContextInitializer { - - public Supplier supplier() { - return () -> "Jim Lahey"; - } - - public Function uppercase() { - return s -> s.toUpperCase(); - } - - public Function lowercase() { - return s -> s.toLowerCase(); - } - - public Function reverse() { - return s -> { - return new StringBuilder(s).reverse().toString(); - }; - } - - @Override - public void initialize(GenericApplicationContext applicationContext) { - - applicationContext.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(uppercase()) - .type(FunctionTypeUtils.functionType(String.class, String.class))); - applicationContext.registerBean("reverse", FunctionRegistration.class, - () -> new FunctionRegistration<>(reverse()) - .type(FunctionTypeUtils.functionType(String.class, String.class))); - applicationContext.registerBean("lowercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(lowercase()) - .type(FunctionTypeUtils.functionType(String.class, String.class))); - applicationContext.registerBean("supplier", FunctionRegistration.class, - () -> new FunctionRegistration<>(supplier()) - .type(FunctionTypeUtils.supplierType(String.class))); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/UserSubmittedTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/UserSubmittedTests.java deleted file mode 100644 index dfbe5413a..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/UserSubmittedTests.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2019-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.function; - -import java.net.URI; -import java.util.function.Function; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * @author Chris Bono - * @since 2.1 - */ -public class UserSubmittedTests { - - @Test - public void testIssue274() throws Exception { - ConfigurableApplicationContext context = SpringApplication.run(Issue274Configuration.class, "--server.port=0"); - int port = context.getEnvironment().getProperty("local.server.port", Integer.class); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - Thread.sleep(200); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/echo"), "", String.class); - assertThat(response.getBody()).isNull(); - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - @Test - public void testIssue274WithData() throws Exception { - ConfigurableApplicationContext context = SpringApplication.run(Issue274Configuration.class, "--server.port=0"); - int port = context.getEnvironment().getProperty("local.server.port", Integer.class); - TestRestTemplate testRestTemplate = new TestRestTemplate(); - Thread.sleep(200); - ResponseEntity response = testRestTemplate - .postForEntity(new URI("http://localhost:" + port + "/echo"), "hello", String.class); - assertThat(response.getBody()).isEqualTo("HELLO"); - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - - @SpringBootApplication - protected static class Issue274Configuration { - - @Bean - public Function echo() { - return s -> s.toUpperCase(); - } - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/DefaultRouteTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/DefaultRouteTests.java deleted file mode 100644 index e441eb5d9..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/DefaultRouteTests.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.util.function.Function; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.mvc.DefaultRouteTests.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "") -@ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) -public class DefaultRouteTests { - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Test - @Disabled("FIXME") - public void explicit() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/uppercase")).body("foo"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("FOO"); - } - - @Test - @Disabled("FIXME") - public void implicit() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.post(new URI("/")).body("foo"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("FOO"); - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - protected static class TestConfiguration { - - @Bean - public Function, Flux> uppercase() { - return flux -> flux.map(value -> value.toUpperCase()); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/GeneralIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/GeneralIntegrationTests.java deleted file mode 100644 index da20059df..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/GeneralIntegrationTests.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2023-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; - -import static org.assertj.core.api.Assertions.assertThat; -/** - * - * @author Oleg Zhurakousky - */ -public class GeneralIntegrationTests { - - @Test - public void testMappedAndUnmappedDeleteFunction() throws Exception { - ApplicationContext context = SpringApplication.run(MultipleConsumerConfiguration.class, "--server.port=0", - "--spring.cloud.function.http.DELETE=consumer2;supplier;function|consumer1"); - String port = context.getEnvironment().getProperty("local.server.port"); - TestRestTemplate template = new TestRestTemplate(); - - ResponseEntity result = template.exchange( - RequestEntity.delete(new URI("http://localhost:" + port + "/consumer1")) - .build(), Void.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - - result = template.exchange( - RequestEntity.delete(new URI("http://localhost:" + port + "/consumer2")) - .build(), Void.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); - - result = template.exchange( - RequestEntity.delete(new URI("http://localhost:" + port + "/function,consumer1")) - .build(), Void.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); - - result = template.exchange( - RequestEntity.delete(new URI("http://localhost:" + port + "/supplier")) - .build(), Void.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - } - - @Test - public void testMappedAndUnmappedPostPutFunction() throws Exception { - ApplicationContext context = SpringApplication.run(MultipleConsumerConfiguration.class, "--server.port=0", - "--spring.cloud.function.http.POST=consumer2;function;supplier;function|consumer1"); - String port = context.getEnvironment().getProperty("local.server.port"); - TestRestTemplate template = new TestRestTemplate(); - - ResponseEntity result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/consumer1")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - - result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/consumer2")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(result.getBody()).isNull(); - - result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/function,consumer1")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(result.getBody()).isNull(); - - result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/function")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - - result = template.exchange(RequestEntity - .post(new URI("http://localhost:" + port + "/supplier")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - } - - - @EnableAutoConfiguration - protected static class MultipleConsumerConfiguration { - - @Bean - public Consumer consumer1() { - return v -> { }; - } - - @Bean - public Consumer consumer2() { - return v -> { }; - } - - @Bean - public Function function() { - return v -> v; - } - - @Bean - public Supplier supplier() { - return () -> ""; - } - } -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HeadersToMessageTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HeadersToMessageTests.java deleted file mode 100644 index 14c264aee..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HeadersToMessageTests.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.function.Function; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.mvc.HeadersToMessageTests.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpEntity; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Oleg Zhurakousky - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.main.web-application-type=servlet", - "spring.cloud.function.web.path=/functions" }) -@ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) -public class HeadersToMessageTests { - - @Autowired - private TestRestTemplate rest; - - @Test - public void testBodyAndCustomHeaderFromMessagePropagation() throws Exception { - HttpEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/employee")) - .contentType(MediaType.APPLICATION_JSON) - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); - assertThat(postForEntity.getBody()).isEqualTo("{\"name\":\"Bob\",\"age\":25}"); - assertThat(postForEntity.getHeaders().containsKey("x-content-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-content-type").get(0)) - .isEqualTo("application/xml"); - assertThat(postForEntity.getHeaders().get("foo").get(0)).isEqualTo("bar"); - } - - @Test - public void testHeadersPropagatedByDefault() throws Exception { - HttpEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/vanilla")) - .contentType(MediaType.APPLICATION_JSON) - .header("x-context-type", "rubbish") - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); - assertThat(postForEntity.getBody()) - .isEqualTo("{\"name\":\"Bob\",\"age\":25,\"foo\":\"bar\"}"); - assertThat(postForEntity.getHeaders().containsKey("x-context-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-context-type").get(0)) - .isEqualTo("rubbish"); - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - protected static class TestConfiguration { - - @Bean({ "employee" }) - public Function>, Message>> function() { - return request -> { - Message> message = MessageBuilder - .withPayload(request.getPayload()) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); - return message; - }; - } - - @Bean - public Function, Map> vanilla() { - return request -> { - Map message = new LinkedHashMap<>(request); - message.put("foo", "bar"); - return message; - }; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpDeleteIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpDeleteIntegrationTests.java deleted file mode 100644 index 834edd2da..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpDeleteIntegrationTests.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.web.mvc.HttpDeleteIntegrationTests.ApplicationConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.messaging.Message; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Oleg Zhurakousky - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=servlet") -@ContextConfiguration(classes = {ApplicationConfiguration.class}) -public class HttpDeleteIntegrationTests { - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Autowired - private ApplicationConfiguration test; - - @BeforeEach - public void init() { - this.test.list.clear(); - } - - @Test - public void testDeleteConsumer() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.delete(new URI("/deleteConsumer/123")) - .build(), Void.class); - - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); - } - - @Test - public void testDeleteConsumerWithParameters() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.delete(new URI("/deleteConsumerAsMessage/123?foo=bar")) - .build(), Void.class); - - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); - } - - @Test - public void testDeleteWithFunction() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.delete(new URI("/deleteFunction")) - .build(), Void.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - } - - @EnableAutoConfiguration - @TestConfiguration - public static class ApplicationConfiguration { - - private List list = new ArrayList<>(); - - public static void main(String[] args) throws Exception { - SpringApplication.run(HttpDeleteIntegrationTests.ApplicationConfiguration.class, - args); - } - - @Bean - public Function deleteFunction() { - return v -> { - assertThat(v).isEqualTo("123"); - System.out.println("Deleting: " + v); - return null; - }; - } - - - @Bean - public Consumer deleteConsumer() { - return v -> { - assertThat(v).isEqualTo("123"); - System.out.println("Deleting: " + v); - }; - } - - @Bean - public Consumer> deleteConsumerAsMessage() { - return v -> { - assertThat(v.getPayload()).isEqualTo("123"); - assertThat(((Map) v.getHeaders().get("http_request_param")).get("foo")).isEqualTo("bar"); - System.out.println("Deleting: " + v); - }; - } - - } - - public static class Foo { - - private String value; - - public Foo(String value) { - this.value = value; - } - - Foo() { - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpGetIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpGetIntegrationTests.java deleted file mode 100644 index d1629d0ec..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpGetIntegrationTests.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright 2012-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.net.URISyntaxException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.mvc.HttpGetIntegrationTests.ApplicationConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * @author Chris Bono - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=servlet") -@ContextConfiguration(classes = { RestApplication.class, ApplicationConfiguration.class }) -@DirtiesContext -public class HttpGetIntegrationTests { - - private static final MediaType EVENT_STREAM = MediaType.TEXT_EVENT_STREAM; - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Autowired - private ApplicationConfiguration test; - - @BeforeEach - public void init() { - this.test.list.clear(); - } - - @Test - public void staticResource() { - assertThat(this.rest.getForObject("/test.html", String.class)) - .contains("Test"); - } - - @Test - public void wordsSSE() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.get(new URI("/words")).accept(EVENT_STREAM).build(), - String.class).getBody()).isEqualTo(sse("foo", "bar")); - } - - @Test - public void wordsJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/words")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - @Disabled("Fix error handling") - public void errorJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/bang")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); - } - - @Test - public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void word() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/word")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("foo"); - } - - @ParameterizedTest - @ValueSource(strings = {"[hello", "hello]", "[hello]"}) - void textContentTypeWithValueWrappedBracketsIsOk(String inputMessagePayloadValue) throws URISyntaxException { - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/echo")) - .contentType(MediaType.TEXT_PLAIN) - .body(inputMessagePayloadValue), String.class); - assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(postForEntity.getBody()).isEqualTo(inputMessagePayloadValue); - } - - @Test - public void foos() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/foos")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); - } - - @Test - public void getMore() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/get/more")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void bareWords() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/bareWords")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void timeoutJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/timeout")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[\"foo\"]"); - } - - @Test - public void emptyJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/empty")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[]"); - } - - @Test - public void sentences() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/sentences")).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - } - - @Test - public void sentencesAcceptAny() throws Exception { - assertThat( - this.rest - .exchange(RequestEntity.get(new URI("/sentences")) - .accept(MediaType.ALL).build(), String.class) - .getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - } - - @Test - public void sentencesAcceptJson() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.get(new URI("/sentences")) - .accept(MediaType.APPLICATION_JSON).build(), - String.class); - assertThat(result.getBody()).isEqualTo("[[\"go\",\"home\"],[\"come\",\"back\"]]"); - assertThat(result.getHeaders().getContentType()) - .isGreaterThanOrEqualTo(MediaType.APPLICATION_JSON); - } - - @Test - @Disabled - public void sentencesAcceptSse() throws Exception { - Thread.sleep(1000); - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/sentences")).accept(EVENT_STREAM).build(), - String.class); - assertThat(result.getBody()) - .isEqualTo(sse("[\"go\",\"home\"]", "[\"come\",\"back\"]")); - assertThat(result.getHeaders().getContentType().isCompatibleWith(EVENT_STREAM)) - .isTrue(); - } - - @Test - public void postMoreFoo() { - assertThat(this.rest.getForObject("/post/more/foo", String.class)) - .isEqualTo("[\"(FOO)\"]"); - } - - @Test - public void uppercaseGet() { - assertThat(this.rest.getForObject("/uppercase/foo", String.class)) - .isEqualTo("[\"(FOO)\"]"); - } - - @Test - public void convertGet() { - assertThat(this.rest.getForObject("/wrap/123", String.class)) - .isEqualTo("[\"..123..\"]"); - } - - @Test - public void supplierFirst() { - assertThat(this.rest.getForObject("/not/a/function", String.class)) - .isEqualTo("[\"hello\"]"); - } - - @Test - public void convertGetJson() throws Exception { - assertThat(this.rest - .exchange(RequestEntity.get(new URI("/entity/321")) - .accept(MediaType.APPLICATION_JSON).build(), String.class) - .getBody()).isEqualTo("[{\"value\":321}]"); - } - - @Test - @Disabled - // this test is wrong since it is returning Flux while setting CT to TEXT_PLAIN. We can't convert it - public void compose() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .get(new URI("/concat,reverse/foo")).accept(MediaType.TEXT_PLAIN).build(), - String.class); - assertThat(result.getBody()).isEqualTo("oofoof"); - } - - private String sse(String... values) { - return "data:" + StringUtils.arrayToDelimitedString(values, "\n\ndata:") + "\n\n"; - } - - @EnableAutoConfiguration - @TestConfiguration - public static class ApplicationConfiguration { - - private List list = new ArrayList<>(); - - public static void main(String[] args) throws Exception { - SpringApplication.run(HttpGetIntegrationTests.ApplicationConfiguration.class, - args); - } - - @Bean - public Function, Flux> concat() { - return flux -> flux.map(v -> v + v); - } - - @Bean - public Function, Flux> reverse() { - return flux -> flux.log() - .map(value -> new StringBuilder(value.trim()).reverse().toString()); - } - - @Bean({ "uppercase", "post/more" }) - public Function, Flux> uppercase() { - return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase() + ")"); - } - - @Bean - public Function, Flux> wrap() { - return flux -> flux.log().map(value -> ".." + value + ".."); - } - - @Bean - public Function, Flux>> entity() { - return flux -> flux.log() - .map(value -> Collections.singletonMap("value", value)); - } - - @Bean({ "words", "get/more" }) - public Supplier> words() { - return () -> Flux.just("foo", "bar"); - } - - @Bean - public Supplier word() { - return () -> "foo"; - } - - @Bean - public Function echo() { - return (input) -> input; - } - - @Bean - public Supplier> foos() { - return () -> Flux.just(new Foo("foo"), new Foo("bar")); - } - - @Bean - public Supplier> bareWords() { - return () -> Arrays.asList("foo", "bar"); - } - - @Bean - public Supplier> bang() { - return () -> Flux.fromArray(new String[] { "foo", "bar" }).map(value -> { - if (value.equals("bar")) { - throw new RuntimeException("Bar"); - } - return value; - }); - } - - @Bean - public Supplier> empty() { - return () -> Flux.fromIterable(Collections.emptyList()); - } - - @Bean("not/a/function") - public Supplier> supplier() { - return () -> Flux.just("hello"); - } - - @Bean("not/a") - public Function, Flux> function() { - return input -> Flux.just("bye"); - } - - @Bean - public Supplier> timeout() { - return () -> Flux.defer(() -> Flux.create(emitter -> { - emitter.next("foo"); - }).timeout(Duration.ofMillis(1000L), Flux.empty())); - } - - @Bean - public Supplier>> sentences() { - return () -> Flux.just(Arrays.asList("go", "home"), - Arrays.asList("come", "back")); - } - - @Bean - public Function, Map> sum() { - return valueMap -> valueMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, values -> values - .getValue().stream().mapToInt(Integer::parseInt).sum())); - } - - } - - public static class Foo { - - private String value; - - public Foo(String value) { - this.value = value; - } - - Foo() { - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpPostIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpPostIntegrationTests.java deleted file mode 100644 index a12c46dcf..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpPostIntegrationTests.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.mvc.HttpPostIntegrationTests.ApplicationConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=servlet") -@ContextConfiguration(classes = { RestApplication.class, ApplicationConfiguration.class }) -public class HttpPostIntegrationTests { - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate rest; - - @Autowired - private ApplicationConfiguration test; - - @BeforeEach - public void init() { - this.test.list.clear(); - } - - @Test - @Disabled - public void qualifierFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/foos")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"[FOO]\"},{\"value\":\"[BAR]\"}]"); - } - - @Test - public void updates() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\", \"two\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isNull(); - } - - @Test - public void updatesJson() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/updates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isEqualTo(null); - } - - @Test - public void addFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/addFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - assertThat(result.getBody()).isEqualTo(null); - } - - @Test - public void bareUpdates() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpdates")).contentType(MediaType.APPLICATION_JSON) - .body("[\"one\",\"two\"]"), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED); - assertThat(this.test.list).hasSize(2); - } - - @Test - public void uppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - public void messages() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/messages")).contentType(MediaType.APPLICATION_JSON) - // Remove this when Spring 5.0.8 is used - .accept(MediaType.valueOf("application/stream+json")) - .header("x-foo", "bar").body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getHeaders().getFirst("x-foo")).isEqualTo("bar"); - assertThat(result.getHeaders()).doesNotContainKey("id"); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - public void headers() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/headers")).contentType(MediaType.APPLICATION_JSON) - // Remove this when Spring 5.0.8 is used - .accept(MediaType.valueOf("application/stream+json")) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getHeaders().getFirst("foo")).isEqualTo("bar"); - assertThat(result.getHeaders()).doesNotContainKey("id"); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - public void uppercaseSingleValue() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/uppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo"), - String.class); - // Result is multi-valued so it has to come out as an array - assertThat(result.getBody()).isEqualTo("[\"(FOO)\"]"); - } - - @Test - @Disabled("WebFlux would split the request body into lines: TODO make this work the same") - public void uppercasePlainText() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.post(new URI("/uppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo\nbar"), - String.class); - assertThat(result.getBody()).isEqualTo("(FOO)(BAR)"); - } - - @Test - public void uppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - public void uppercaseFoo() throws Exception { - // Single Foo can be parsed - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/upFoos")).contentType(MediaType.APPLICATION_JSON) - .body("{\"value\":\"foo\"}"), String.class); - assertThat(result.getBody()).isEqualTo("[{\"value\":\"FOO\"}]"); - } - - @Test - public void bareUppercaseFoos() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpFoos")).contentType(MediaType.APPLICATION_JSON) - .body("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"), String.class); - assertThat(result.getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - public void bareUppercaseFoo() throws Exception { - // Single Foo can be parsed and returns a single value if the function is defined - // that way - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUpFoos")).contentType(MediaType.APPLICATION_JSON) - .body("{\"value\":\"foo\"}"), String.class); - assertThat(result.getBody()).isEqualTo("{\"value\":\"FOO\"}"); - } - - @Test - public void bareUppercase() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/bareUppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - public void singleValuedText() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/bareUppercase")) - .contentType(MediaType.TEXT_PLAIN).body("foo"), - String.class); - assertThat(result.getBody()).isEqualTo("(FOO)"); - } - - @Test - public void transform() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/transform")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - public void postMore() throws Exception { - ResponseEntity result = this.rest.exchange(RequestEntity - .post(new URI("/post/more")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class); - assertThat(result.getBody()).isEqualTo("[\"(FOO)\",\"(BAR)\"]"); - } - - @Test - public void convertPost() throws Exception { - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/wrap")) - .contentType(MediaType.TEXT_PLAIN).body("123"), - String.class); - // Result is multi-valued so it has to come out as an array - assertThat(result.getBody()).isEqualTo("[\"..123..\"]"); - } - - @Test - public void convertPostJson() throws Exception { - // If you POST a single value to a Function,Flux> it can't - // determine if the output is single valued, so it has to send an array back - ResponseEntity result = this.rest - .exchange( - RequestEntity.post(new URI("/doubler")) - .contentType(MediaType.TEXT_PLAIN).body("123"), - String.class); - assertThat(result.getBody()).isEqualTo("[246]"); - } - - @Test - public void uppercaseJsonArray() throws Exception { - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/maps")) - .contentType(MediaType.APPLICATION_JSON) - // The new line in the middle is optional - .body("[{\"value\":\"foo\"},\n{\"value\":\"bar\"}]"), - String.class).getBody()) - .isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); - } - - @Test - public void uppercaseSSE() throws Exception { - assertThat(this.rest.exchange(RequestEntity.post(new URI("/uppercase")).contentType(MediaType.APPLICATION_JSON) - .body("[\"foo\",\"bar\"]"), String.class).getBody()) - .isEqualTo(sse("(FOO)", "(BAR)")); - } - - @Test - public void sum() throws Exception { - - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - - map.put("A", Arrays.asList("1", "2", "3")); - map.put("B", Arrays.asList("5", "6")); - - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/sum")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); - } - - @Test - public void multipart() throws Exception { - - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - - map.put("A", Arrays.asList("1", "2", "3")); - map.put("B", Arrays.asList("5", "6")); - - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/sum")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.MULTIPART_FORM_DATA).body(map), - String.class).getBody()).isEqualTo("{\"A\":6,\"B\":11}"); - } - - @Test - public void count() throws Exception { - List list = Arrays.asList("A", "B", "A"); - assertThat(this.rest.exchange( - RequestEntity.post(new URI("/count")).accept(MediaType.APPLICATION_JSON) - .contentType(MediaType.APPLICATION_JSON).body(list), - String.class).getBody()).isEqualTo("{\"A\":2,\"B\":1}"); - } - - private String sse(String... values) { - return "[\"" + StringUtils.arrayToDelimitedString(values, "\",\"") + "\"]"; - } - - @EnableAutoConfiguration - @TestConfiguration - public static class ApplicationConfiguration { - - private List list = new ArrayList<>(); - - public static void main(String[] args) throws Exception { - SpringApplication.run(HttpPostIntegrationTests.ApplicationConfiguration.class, - args); - } - - @Bean({ "uppercase", "transform", "post/more" }) - public Function, Flux> uppercase() { - return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase() + ")"); - } - - @Bean - public Function bareUppercase() { - return value -> "(" + value.trim().toUpperCase() + ")"; - } - - @Bean - public Function, Message> messages() { - return value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase() + ")") - .copyHeaders(value.getHeaders()).build(); - } - - @Bean - public Function>, Flux>> headers() { - return flux -> flux.map(value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase() + ")") - .setHeader("foo", "bar").build()); - } - - @Bean - public Function, Flux> upFoos() { - return flux -> flux.log() - .map(value -> new Foo(value.getValue().trim().toUpperCase())); - } - - @Bean - public Function bareUpFoos() { - return value -> new Foo(value.getValue().trim().toUpperCase()); - } - - @Bean - public Function, Flux> wrap() { - return flux -> flux.log().map(value -> ".." + value + ".."); - } - - @Bean - public Function, Flux> doubler() { - return flux -> flux.log().map(value -> 2 * value); - } - - @Bean - public Function>, Flux>> maps() { - return flux -> flux.map(value -> { - value.put("value", value.get("value").trim().toUpperCase()); - return value; - }); - } - - @Bean - @Qualifier("foos") - public Function qualifier() { - return value -> new Foo("[" + value.trim().toUpperCase() + "]"); - } - - @Bean - public Consumer> updates() { - return flux -> flux.subscribe(value -> this.list.add(value)); - } - - @Bean - public Consumer> addFoos() { - return flux -> flux.subscribe(value -> this.list.add(value.getValue())); - } - - @Bean - public Consumer bareUpdates() { - return value -> { - this.list.add(value); - }; - } - - @Bean("not/a") - public Function, Flux> function() { - return input -> Flux.just("bye"); - } - - @Bean - public Function, Map> sum() { - return valueMap -> valueMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, values -> values - .getValue().stream().mapToInt(Integer::parseInt).sum())); - } - - @Bean - public Function, Mono>> count() { - return flux -> flux.collect(HashMap::new, - (map, word) -> map.merge(word, 1, Integer::sum)); - } - - } - - public static class Foo { - - private String value; - - public Foo(String value) { - this.value = value; - } - - Foo() { - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/MultipartFileTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/MultipartFileTests.java deleted file mode 100644 index 5b35a86c4..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/MultipartFileTests.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.util.List; -import java.util.function.Function; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.function.json.JsonMapper; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.core.io.ClassPathResource; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.web.multipart.MultipartFile; - -import static org.assertj.core.api.Assertions.assertThat; -/** - * - * @author Oleg Zhurakousky - * @author Chris Bono - */ -public class MultipartFileTests { - - @Test - public void testMultipartFileUpload() throws Exception { - ApplicationContext context = SpringApplication.run(TestConfiguration.class, "--server.port=0"); - String port = context.getEnvironment().getProperty("local.server.port"); - JsonMapper mapper = context.getBean(JsonMapper.class); - TestRestTemplate template = new TestRestTemplate(); - - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - map.add("file", new ClassPathResource("META-INF/spring.factories")); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - - HttpEntity> requestEntity = new HttpEntity>( - map, headers); - ResponseEntity result = template.exchange(new URI("http://localhost:" + port + "/uppercase"), - HttpMethod.POST, requestEntity, String.class); - List resultCollection = mapper.fromJson(result.getBody(), List.class); - assertThat(resultCollection.get(0)).isEqualTo("SPRING.FACTORIES"); - } - - @Test - public void testMultipartFilesUpload() throws Exception { - ApplicationContext context = SpringApplication.run(TestConfiguration.class, "--server.port=0"); - String port = context.getEnvironment().getProperty("local.server.port"); - JsonMapper mapper = context.getBean(JsonMapper.class); - TestRestTemplate template = new TestRestTemplate(); - - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - map.add("fileA", new ClassPathResource("META-INF/spring.factories")); - map.add("fileB", new ClassPathResource("static/test.html")); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - - HttpEntity> requestEntity = new HttpEntity>( - map, headers); - ResponseEntity result = template.exchange(new URI("http://localhost:" + port + "/uppercase"), - HttpMethod.POST, requestEntity, String.class); - List resultCollection = mapper.fromJson(result.getBody(), List.class); - assertThat(resultCollection.get(0)).isEqualTo("SPRING.FACTORIES"); - assertThat(resultCollection.get(1)).isEqualTo("TEST.HTML"); - } - - @EnableAutoConfiguration - protected static class TestConfiguration { - - @Bean - public Function uppercase() { - return value -> { - return value.getOriginalFilename().toUpperCase(); - }; - } - } -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/PrefixTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/PrefixTests.java deleted file mode 100644 index d8b264cb9..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/PrefixTests.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.mvc.PrefixTests.TestConfiguration; -import org.springframework.cloud.function.web.util.HeaderUtils; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.messaging.Message; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.main.web-application-type=servlet", - "spring.cloud.function.web.path=/functions" }) -@ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) -public class PrefixTests { - - @Autowired - private TestRestTemplate rest; - - @Test - public void words() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/functions/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @Test - public void missing() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); - } - - - @Test - public void uppercase() throws Exception { - ResponseEntity result = this.rest.exchange( - RequestEntity.get(new URI("/functions/uppercase/foo?nome=Doe&prenome=John")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - protected static class TestConfiguration { - - @Bean({ "words", "get/more" }) - public Supplier> words() { - return () -> Flux.fromArray(new String[] { "foo", "bar" }); - } - - @Bean - public Function, String[]> uppercase() { - return message -> { - assertThat(message.getPayload().equals("foo")); - Map httpParam = (Map) message.getHeaders().get(HeaderUtils.HTTP_REQUEST_PARAM); - assertThat(httpParam.get("nome")).isEqualTo("Doe"); - assertThat(httpParam.get("prenome")).isEqualTo("John"); - return new String[] { "foo", "bar" }; - }; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/RoutingFunctionTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/RoutingFunctionTests.java deleted file mode 100644 index 53c90d0b2..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/RoutingFunctionTests.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.function.context.FunctionProperties; -import org.springframework.cloud.function.context.config.RoutingFunction; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.mvc.RoutingFunctionTests.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Oleg Zhurakousky - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.main.web-application-type=servlet", - "spring.cloud.function.web.path=/functions", - "spring.cloud.function.routing.enabled=true"}) -@ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) -public class RoutingFunctionTests { - - @Autowired - private TestRestTemplate rest; - - @Autowired - private FunctionProperties functionProperties; - - - @Test - @DirtiesContext - public void testFunctionMessage() throws Exception { - - HttpEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.APPLICATION_JSON) - .header("spring.cloud.function.definition", "employee") - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); - assertThat(postForEntity.getBody()).isEqualTo("{\"name\":\"Bob\",\"age\":25}"); - assertThat(postForEntity.getHeaders().containsKey("x-content-type")).isTrue(); - assertThat(postForEntity.getHeaders().get("x-content-type").get(0)) - .isEqualTo("application/xml"); - assertThat(postForEntity.getHeaders().get("foo").get(0)).isEqualTo("bar"); - } - - @Test - @DirtiesContext - public void testFunctionPrimitive() throws Exception { - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .header("spring.cloud.function.definition", "echo") - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); - postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .header("spring.cloud.function.definition", "echo") - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); - - assertThat(postForEntity.getBody()).isEqualTo("{\"name\":\"Bob\",\"age\":25}"); - assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - @Test - @DirtiesContext - @Disabled // not sure if this test is correct. Why does ? has to be assumed as String? - public void testFluxFunctionPrimitive() throws Exception { - this.functionProperties.setDefinition("fluxuppercase"); - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .body("[\"hello\", \"bye\"]"), String.class); - assertThat(postForEntity.getBody()).isEqualTo("[\"HELLO\", \"BYE\"]"); - assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - - postForEntity = this.rest.exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .body("hello1"), String.class); - assertThat(postForEntity.getBody()).isEqualTo("HELLO1"); - assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - -// postForEntity = this.rest.exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) -// .contentType(MediaType.TEXT_PLAIN) -// .body("hello2"), String.class); -// assertThat(postForEntity.getBody()).isEqualTo("HELLO2"); -// assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - @Test - @DirtiesContext - public void testFluxFunctionPrimitiveArray() throws Exception { - this.functionProperties.setDefinition("fluxuppercase"); - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.APPLICATION_JSON) - .body(new String[] {"a", "b", "c"}), String.class); - assertThat(postForEntity.getBody()).isEqualTo("[\"A\",\"B\",\"C\"]"); - assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - @Test - @DirtiesContext - @Disabled - public void testFluxConsumer() throws Exception { - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.APPLICATION_JSON) - .header("function.name", "fluxconsumer") - .body(new String[] {"a", "b", "c"}), String.class); - assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - - } - - - @Test - @DirtiesContext - @Disabled - public void testFunctionPojo() throws Exception { - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.APPLICATION_JSON) - .header("function.name", "echoPojo") - .body("{\"value\":\"foo\"}"), String.class); - assertThat(postForEntity.getBody()).isEqualTo("{\"foo\":{\"value\":\"foo\"},\"value\":\"bar\"}"); - assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - @Test - @DirtiesContext - @Disabled - public void testConsumerMessage() throws Exception { - ResponseEntity postForEntity = this.rest - .exchange(RequestEntity.post(new URI("/functions/" + RoutingFunction.FUNCTION_NAME)) - .contentType(MediaType.TEXT_PLAIN) - .header("spring.cloud.function.definition", "messageConsumer") - .body("{\"name\":\"Bob\",\"age\":25}"), String.class); - assertThat(postForEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - protected static class TestConfiguration { - - @Bean({ "employee" }) - public Function>, Message>> function() { - return request -> { - Message> message = MessageBuilder - .withPayload(request.getPayload()) - .setHeader("X-Content-Type", "application/xml") - .setHeader("foo", "bar").build(); - return message; - }; - } - - @Bean - public Consumer> messageConsumer() { - return value -> System.out.println("Value: " + value); - } - - @Bean - public Function echo() { - return v -> v; - } - - @Bean - public Function, Flux> fluxuppercase() { - return v -> v.map(s -> { - System.out.println(s); - return s.toUpperCase(); - }); - } - - @Bean - public Consumer> fluxconsumer() { - return flux -> flux.doOnNext(s -> { - System.out.println("Received: " + s); - }).subscribe(); - } - - @Bean - public Function echoPojo() { - return v -> { - Bar bar = new Bar(); - bar.setFoo(v); - bar.setValue("bar"); - return bar; - }; - } - - } - - public static class Foo { - private String value; - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - } - - public static class Bar { - private Foo foo; - private String value; - public Foo getFoo() { - return foo; - } - public void setFoo(Foo foo) { - this.foo = foo; - } - public String getValue() { - return value; - } - public void setValue(String value) { - this.value = value; - } - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/SingletonTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/SingletonTests.java deleted file mode 100644 index 5636d5171..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/SingletonTests.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.mvc; - -import java.net.URI; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.mvc.SingletonTests.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@ContextConfiguration(classes = { RestApplication.class, TestConfiguration.class }) -public class SingletonTests { - - @Autowired - private TestRestTemplate rest; - - @Test - public void words() throws Exception { - ResponseEntity result = this.rest - .exchange(RequestEntity.get(new URI("/words")).build(), String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("[\"foo\",\"bar\"]"); - } - - @EnableAutoConfiguration - @org.springframework.boot.test.context.TestConfiguration - protected static class TestConfiguration { - - @Bean - public static BeanDefinitionRegistryPostProcessor processor() { - return new BeanDefinitionRegistryPostProcessor() { - - @Override - public void postProcessBeanFactory( - ConfigurableListableBeanFactory beanFactory) - throws BeansException { - } - - @Override - public void postProcessBeanDefinitionRegistry( - BeanDefinitionRegistry registry) throws BeansException { - // Simulates what happens when you add a compiled function - RootBeanDefinition beanDefinition = new RootBeanDefinition( - MySupplier.class); - registry.registerBeanDefinition("words", beanDefinition); - } - }; - } - - } - - static class MySupplier implements Supplier> { - - @Override - public Flux get() { - return Flux.just("foo", "bar"); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationIntegrationTests.java deleted file mode 100644 index f2cfe400d..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationIntegrationTests.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2018-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.function.web.TestSocketUtils; -import org.springframework.cloud.function.web.source.FunctionAutoConfigurationIntegrationTests.ApplicationConfiguration; -import org.springframework.cloud.function.web.source.FunctionAutoConfigurationIntegrationTests.RestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@SpringBootTest(classes = { RestConfiguration.class, - ApplicationConfiguration.class }, webEnvironment = WebEnvironment.DEFINED_PORT, properties = { - "spring.cloud.function.web.export.sink.url=http://localhost:${server.port}", - "spring.cloud.function.web.export.source.url=http://localhost:${server.port}", - "spring.cloud.function.web.export.sink.name=origin|uppercase" - // "spring.cloud.function.web.export.debug=true" - }) -@Disabled -public class FunctionAutoConfigurationIntegrationTests { - - @Autowired - private SupplierExporter forwarder; - - @Autowired - private RestConfiguration app; - - @BeforeAll - public static void init() { - System.setProperty("server.port", "" + TestSocketUtils.findAvailableTcpPort()); - } - - @AfterAll - public static void close() { - System.clearProperty("server.port"); - } - - @Test - public void copiesMessages() throws Exception { - int count = 0; - while (this.forwarder.isRunning() && count++ < 10) { - Thread.sleep(20); - } - // It completed - assertThat(this.forwarder.isOk()).isTrue(); - assertThat(this.app.inputs).contains("\"HELLO\""); - assertThat(this.app.inputs).contains("\"WORLD\""); - } - - @EnableAutoConfiguration - @TestConfiguration - public static class ApplicationConfiguration { - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } - - } - - @TestConfiguration - @RestController - public static class RestConfiguration { - - private static Log logger = LogFactory.getLog(RestConfiguration.class); - - private List inputs = new ArrayList<>(); - - private Iterator outputs = Arrays.asList("hello", "world").iterator(); - - @GetMapping("/") - ResponseEntity home() { - logger.info("HOME"); - if (this.outputs.hasNext()) { - return ResponseEntity.ok(this.outputs.next()); - } - return ResponseEntity.notFound().build(); - } - - @PostMapping("/") - void accept(@RequestBody String body) { - logger.info("ACCEPT"); - this.inputs.add(body); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationWithRetriesIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationWithRetriesIntegrationTests.java deleted file mode 100644 index 4ded82a1c..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationWithRetriesIntegrationTests.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.function.web.TestSocketUtils; -import org.springframework.cloud.function.web.source.FunctionAutoConfigurationWithRetriesIntegrationTests.ApplicationConfiguration; -import org.springframework.cloud.function.web.source.FunctionAutoConfigurationWithRetriesIntegrationTests.RestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Oleg Zhurakousky - * - */ -@SpringBootTest(classes = { RestConfiguration.class, - ApplicationConfiguration.class }, - webEnvironment = WebEnvironment.DEFINED_PORT, properties = { - "spring.cloud.function.web.export.sink.url=http://localhost:${server.port}", - "spring.cloud.function.web.export.source.url=http://localhost:${server.port}", - "spring.cloud.function.web.export.sink.name=origin|uppercase", - "spring.cloud.function.web.export.debug=true"}) -public class FunctionAutoConfigurationWithRetriesIntegrationTests { - - @Autowired - private SupplierExporter forwarder; - - @Autowired - private RestConfiguration app; - - @BeforeAll - public static void init() { - System.setProperty("server.port", "" + TestSocketUtils.findAvailableTcpPort()); - } - - @AfterAll - public static void close() { - System.clearProperty("server.port"); - } - - @Test - @Disabled - public void copiesMessages() throws Exception { - int count = 0; - while (this.forwarder.isRunning() && count++ < 30) { - Thread.sleep(200); - } - // It completed - assertThat(this.forwarder.isOk()).isTrue(); - assertThat(this.forwarder.isRunning()).isFalse(); - assertThat(this.app.inputs.size()).isEqualTo(4); - assertThat(this.app.inputs).contains("2"); - assertThat(this.app.inputs).contains("4"); - assertThat(this.app.inputs).contains("6"); - assertThat(this.app.inputs).contains("8"); - } - - @EnableAutoConfiguration - @TestConfiguration - public static class ApplicationConfiguration { - - @Bean - public Function uppercase() { - return value -> value.toUpperCase(); - } - - } - - @TestConfiguration - @RestController - public static class RestConfiguration { - - @Autowired - private SupplierExporter forwarder; - - private static Log logger = LogFactory.getLog(RestConfiguration.class); - - private List inputs = new ArrayList<>(); - - private int counter; - - @GetMapping("/") - ResponseEntity home() { - logger.info("HOME"); - if (++counter % 2 == 0 && counter < 10) { - return ResponseEntity.ok(String.valueOf(counter)); - } - if (counter >= 10) { - forwarder.stop(); - } - return ResponseEntity.notFound().build(); - } - - @PostMapping("/") - void accept(@RequestBody String body) { - logger.info("ACCEPT"); - this.inputs.add(body); - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/SourceAutoConfigurationIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/SourceAutoConfigurationIntegrationTests.java deleted file mode 100644 index 600bc728c..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/SourceAutoConfigurationIntegrationTests.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.util.function.Supplier; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.function.web.source.SourceAutoConfigurationIntegrationTests.ApplicationConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -// @formatter:off -@SpringBootTest(webEnvironment = WebEnvironment.NONE, - properties = "spring.cloud.function.web.export.sink.url=https://nosuchhost") -// @formatter:on -@ContextConfiguration(classes = { ApplicationConfiguration.class }) -@Disabled -public class SourceAutoConfigurationIntegrationTests { - - @Autowired - private SupplierExporter forwarder; - - @Test - public void fails() throws Exception { - int count = 0; - while (this.forwarder.isRunning() && count++ < 1000) { - Thread.sleep(50); - } - // It completed - assertThat(this.forwarder.isRunning()).isFalse(); - // But failed - assertThat(this.forwarder.isOk()).isFalse(); - } - - @EnableAutoConfiguration - @TestConfiguration - public static class ApplicationConfiguration { - - @Bean - public Supplier word() { - return () -> "foo"; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/WebAppIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/WebAppIntegrationTests.java deleted file mode 100644 index a768fc537..000000000 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/WebAppIntegrationTests.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.web.source; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.function.web.RestApplication; -import org.springframework.cloud.function.web.TestSocketUtils; -import org.springframework.cloud.function.web.source.WebAppIntegrationTests.ApplicationConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -@SpringBootTest(classes = { RestApplication.class, ApplicationConfiguration.class }, - webEnvironment = WebEnvironment.DEFINED_PORT, properties = { - "spring.main.web-application-type=reactive", - "spring.cloud.function.web.export.sink.url=http://localhost:${server.port}/values", - // manually so we know the webapp is listening when we start - "spring.cloud.function.web.export.autoStartup=false" }) -public class WebAppIntegrationTests { - - private static Log logger = LogFactory.getLog(WebAppIntegrationTests.class); - - @Autowired - private SupplierExporter forwarder; - - @Autowired - private ApplicationConfiguration app; - - @BeforeAll - public static void init() { - System.setProperty("server.port", "" + TestSocketUtils.findAvailableTcpPort()); - } - - @AfterAll - public static void close() { - System.clearProperty("server.port"); - } - - @Test - @Disabled - public void posts() throws Exception { - this.forwarder.start(); - this.app.latch.await(10, TimeUnit.SECONDS); - assertThat(this.app.values).hasSize(1); - } - - @EnableAutoConfiguration - @TestConfiguration - @RestController - public static class ApplicationConfiguration { - - private List values = new ArrayList<>(); - - private CountDownLatch latch = new CountDownLatch(1); - - @Bean - public Supplier word() { - return () -> "foo"; - } - - // An endpoint to catch the values being exported - @PostMapping("/values") - public String value(@RequestBody String body) { - logger.info("Body: " + body); - this.values.add(body); - this.latch.countDown(); - return "ok"; - } - - } - -} diff --git a/spring-cloud-function-web/src/test/resources/static/test.html b/spring-cloud-function-web/src/test/resources/static/test.html deleted file mode 100644 index d64d7739a..000000000 --- a/spring-cloud-function-web/src/test/resources/static/test.html +++ /dev/null @@ -1,3 +0,0 @@ - -Test - diff --git a/spring-cloud-starter-function-web/pom.xml b/spring-cloud-starter-function-web/pom.xml deleted file mode 100644 index bf78f1fd8..000000000 --- a/spring-cloud-starter-function-web/pom.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - 4.0.0 - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - .. - - spring-cloud-starter-function-web - spring-cloud-starter-starter-function-web - Spring Cloud Starter - https://projects.spring.io/spring-cloud - - Pivotal Software, Inc. - https://www.spring.io - - - - org.springframework.cloud - spring-cloud-function-web - - - org.springframework.boot - spring-boot-starter-web - - - diff --git a/spring-cloud-starter-function-webflux/pom.xml b/spring-cloud-starter-function-webflux/pom.xml deleted file mode 100644 index c64452d12..000000000 --- a/spring-cloud-starter-function-webflux/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - 4.0.0 - - org.springframework.cloud - spring-cloud-function-parent - 4.1.0-SNAPSHOT - - spring-cloud-starter-function-webflux - spring-cloud-starter-function-webflux - Spring Cloud Starter - https://projects.spring.io/spring-cloud - - Pivotal Software, Inc. - https://www.spring.io - - - - org.springframework.cloud - spring-cloud-function-web - - - org.springframework.boot - spring-boot-starter-webflux - - - diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml deleted file mode 100644 index f0be38384..000000000 --- a/src/checkstyle/checkstyle-suppressions.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/supplemental-ui/partials/nav-search.hbs b/supplemental-ui/partials/nav-search.hbs new file mode 100644 index 000000000..5e3795528 --- /dev/null +++ b/supplemental-ui/partials/nav-search.hbs @@ -0,0 +1,11 @@ +{{#if env.ALGOLIA_API_KEY}} +

+{{/if}} diff --git a/supplemental-ui/partials/search.hbs b/supplemental-ui/partials/search.hbs new file mode 100644 index 000000000..384e505f6 --- /dev/null +++ b/supplemental-ui/partials/search.hbs @@ -0,0 +1,27 @@ + +{{#if env.ALGOLIA_API_KEY}} + + + + + + + + +{{/if}}