Compare commits
78 Commits
Author | SHA1 | Date |
---|---|---|
|
0d96691f6d | |
|
c8a22b5175 | |
|
57f9867a5c | |
|
43f0d5b138 | |
|
efe7e01b75 | |
|
188c3c7085 | |
|
659bb8cc01 | |
|
9981635e7d | |
|
b54df5ca0c | |
|
a3decf768c | |
|
215e67f2c3 | |
|
514e00d75b | |
|
f7ac215bf1 | |
|
5f4abd144a | |
|
8d59d29bb1 | |
|
6ab42de007 | |
|
2e87988d0c | |
|
6d958b69d4 | |
|
1de03ad38a | |
|
4c5d0efb89 | |
|
01a9111d8b | |
|
e056d1b2d8 | |
|
efdf0ba866 | |
|
bd90d903ce | |
|
52a98d778c | |
|
010627e784 | |
|
a7904823c3 | |
|
e5a35ac472 | |
|
4d204cef89 | |
|
43afb6ceaa | |
|
9c6e7fd11e | |
|
98deac1599 | |
|
05baf9be8a | |
|
3add823e00 | |
|
5a03173dde | |
|
9ee16fb48c | |
|
0db4446163 | |
|
111fb55cfd | |
|
55fddb35fc | |
|
7b9d020acc | |
|
fb11b94f2b | |
|
eaef3becdd | |
|
b30324e916 | |
|
1f9fa13231 | |
|
a135755ec6 | |
|
677a2c2628 | |
|
826e099fc0 | |
|
76366338fc | |
|
4ef304115a | |
|
582feed520 | |
|
5ef1088a19 | |
|
698cdf7ad4 | |
|
4ebeab0e0f | |
|
4c81f3eacc | |
|
569e025cf0 | |
|
3614a4f5f4 | |
|
d64aff7327 | |
|
d1cff75230 | |
|
1591cb337a | |
|
f71303b7b7 | |
|
d59b33307a | |
|
e0d1961f35 | |
|
7c6b52ab30 | |
|
433ec5b274 | |
|
40fe91a5e0 | |
|
a43f90f4e2 | |
|
e488269510 | |
|
7d50c7fc7a | |
|
f1a86af656 | |
|
354f7a16ef | |
|
9132a13d81 | |
|
a491c85eb2 | |
|
6362bfbcd6 | |
|
f08a099ed9 | |
|
4139fb7e57 | |
|
adde53c817 | |
|
0dc10251ff | |
|
b9eaa2fcaa |
|
@ -0,0 +1,11 @@
|
|||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "maven"
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -4,7 +4,8 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- master
|
||||
- 2.[0-9]+
|
||||
- main
|
||||
- '[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
@ -12,7 +13,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [ 8, 11 ]
|
||||
java: [ 8, 11, 17 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup java
|
||||
|
@ -20,8 +21,9 @@ jobs:
|
|||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- run: |
|
||||
mvn clean install -DskipTests -B
|
||||
mvn verify -B
|
||||
./mvnw clean install -DskipTests -B
|
||||
./mvnw verify -B
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
|
@ -32,13 +34,13 @@ jobs:
|
|||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
server-id: central # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
server-username: MAVEN_USERNAME # env variable for username in deploy
|
||||
server-password: MAVEN_CENTRAL_TOKEN # env variable for token in deploy
|
||||
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
|
||||
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
|
||||
- name: Publish to Apache Maven Central
|
||||
run: mvn clean deploy -Drelease -DskipTests -B
|
||||
run: ./mvnw clean deploy -Drelease -DskipTests -B
|
||||
env:
|
||||
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
|
||||
MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
|
||||
|
|
|
@ -5,6 +5,14 @@ on:
|
|||
version:
|
||||
description: 'Version to bump (without prepending "v")'
|
||||
required: true
|
||||
maven-modules:
|
||||
description: "Whether to bump versions in pom.xml files"
|
||||
type: choice
|
||||
required: true
|
||||
default: 'true'
|
||||
options:
|
||||
- 'true'
|
||||
- 'false'
|
||||
|
||||
jobs:
|
||||
bump:
|
||||
|
@ -19,7 +27,8 @@ jobs:
|
|||
with:
|
||||
java-version: 8
|
||||
- name: Bump version using Maven
|
||||
run: 'mvn versions:set -DnewVersion=$NEW_VERSION -DgenerateBackupPoms=false -B'
|
||||
if: ${{ inputs.maven-modules == 'true' }}
|
||||
run: './mvnw versions:set -DnewVersion=$NEW_VERSION -DgenerateBackupPoms=false -B'
|
||||
- name: Bump version in docs
|
||||
if: ${{ !endsWith(github.event.inputs.version, 'SNAPSHOT') }}
|
||||
run: 'find . -type f -name "*.md" -exec sed -i -e "s+<version>[a-zA-Z0-9.-]*<\/version>+<version>$NEW_VERSION</version>+g" {} +'
|
||||
|
|
|
@ -4,7 +4,8 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 2.[0-9]+
|
||||
- main
|
||||
- '[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
@ -18,9 +19,9 @@ jobs:
|
|||
- name: Setup java
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
cache: 'maven'
|
||||
java-version: ${{ matrix.java }}
|
||||
distribution: 'temurin'
|
||||
- run: |
|
||||
mvn clean install -DskipTests -B
|
||||
mvn verify -B
|
||||
./mvnw clean install -DskipTests -B
|
||||
./mvnw verify -B
|
||||
./mvnw javadoc:javadoc
|
||||
|
|
|
@ -45,3 +45,5 @@ _site/
|
|||
|
||||
# MacOS
|
||||
*.DS_Store
|
||||
/http/restful-ws-jakarta/src/main/*
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ your code. Thanks for getting involved! :rocket:
|
|||
* [Getting Started](#getting-started)
|
||||
* [Branches](#branches)
|
||||
* [Commit Messages](#commit-messages)
|
||||
* [Staying current with master](#staying-current-with-master)
|
||||
* [Staying current with main](#staying-current-with-main)
|
||||
* [Submitting and Updating a Pull Request](#submitting-and-updating-a-pull-request)
|
||||
* [Congratulations!](#congratulations)
|
||||
|
||||
|
@ -47,7 +47,7 @@ organized.
|
|||
|
||||
```console
|
||||
git fetch upstream
|
||||
git reset --hard upstream/master
|
||||
git reset --hard upstream/main
|
||||
git checkout FETCH_HEAD
|
||||
git checkout -b 48-fix-http-agent-error
|
||||
```
|
||||
|
@ -102,19 +102,19 @@ Date: Thu Feb 2 11:41:15 2018 -0800
|
|||
Notice the `Author` and `Signed-off-by` lines match. If they don't your PR will
|
||||
be rejected by the automated DCO check.
|
||||
|
||||
## Staying Current with `master`
|
||||
## Staying Current with `main`
|
||||
|
||||
As you are working on your branch, changes may happen on `master`. Before
|
||||
As you are working on your branch, changes may happen on `main`. Before
|
||||
submitting your pull request, be sure that your branch has been updated
|
||||
with the latest commits.
|
||||
|
||||
```console
|
||||
git fetch upstream
|
||||
git rebase upstream/master
|
||||
git rebase upstream/main
|
||||
```
|
||||
|
||||
This may cause conflicts if the files you are changing on your branch are
|
||||
also changed on master. Error messages from `git` will indicate if conflicts
|
||||
also changed on main. Error messages from `git` will indicate if conflicts
|
||||
exist and what files need attention. Resolve the conflicts in each file, then
|
||||
continue with the rebase with `git rebase --continue`.
|
||||
|
||||
|
@ -131,15 +131,15 @@ git push -f origin 48-fix-http-agent-error
|
|||
Before submitting a pull request, you should make sure that all of the tests
|
||||
successfully pass.
|
||||
|
||||
Once you have sent your pull request, `master` may continue to evolve
|
||||
before your pull request has landed. If there are any commits on `master`
|
||||
Once you have sent your pull request, `main` may continue to evolve
|
||||
before your pull request has landed. If there are any commits on `main`
|
||||
that conflict with your changes, you may need to update your branch with
|
||||
these changes before the pull request can land. Resolve conflicts the same
|
||||
way as before.
|
||||
|
||||
```console
|
||||
git fetch upstream
|
||||
git rebase upstream/master
|
||||
git rebase upstream/main
|
||||
# fix any potential conflicts
|
||||
git push -f origin 48-fix-http-agent-error
|
||||
```
|
||||
|
@ -156,7 +156,7 @@ for details.
|
|||
|
||||
```console
|
||||
git commit -m "fixup: fix typo"
|
||||
git rebase -i upstream/master # follow git instructions
|
||||
git rebase -i upstream/main # follow git instructions
|
||||
```
|
||||
|
||||
Once you have rebased your commits, you can force push to your fork as before.
|
||||
|
|
|
@ -1,57 +1,5 @@
|
|||
# Maintainers
|
||||
|
||||
Currently, active maintainers who may be found in the CNCF Slack.
|
||||
Current active maintainers of this SDK:
|
||||
|
||||
- [Pierangelo Di Pilato](https://github.com/pierDipi)
|
||||
|
||||
# Maintainer's Guide
|
||||
|
||||
## Release Process
|
||||
|
||||
The release process is automated with Github actions. In order to perform a release:
|
||||
|
||||
1. Check if master CI pass.
|
||||
1. Open the Github repository main page and go in the tab "Actions". Trigger the workflow "Bump version" and insert the new version to release. This will create a new release PR.
|
||||
1. Check the release PR, merge it and cleanup the created branch.
|
||||
1. Wait for the CI to complete the deploy of the modules to OSSRH.
|
||||
1. Using the Github UI, create a new release, specifying the release notes and the tag to use.
|
||||
1. Trigger again the workflow "Bump version" to bump versions back to a snapshot version.
|
||||
1. Check the snapshot release PR, merge it and cleanup the created branch.
|
||||
|
||||
## Tips
|
||||
|
||||
Here are a few tips for repository maintainers.
|
||||
|
||||
* Stay on top of your pull requests. PRs that languish for too long can become difficult to merge.
|
||||
* Work from your own fork. As you are making contributions to the project, you should be working from your own fork just as outside contributors do. This keeps the branches in github to a minimum and reduces unnecessary CI runs.
|
||||
* Try to proactively label issues and pull requests with labels
|
||||
* Actively review pull requests as they are submitted
|
||||
* Triage issues once in a while in order to keep the repository alive.
|
||||
* If some issues are stale for too long because they are no longer valid/relevant or because the discussion reached no significant action items to perform, close them and invite the users to reopen if they need it.
|
||||
* If some PRs are no longer valid due to conflicts, but the PR is still needed, ask the contributor to rebase their PR.
|
||||
* If some issues and PRs are still relevant, use labels to help organize tasks.
|
||||
* If you find an issue that you want to create a pull request for, be sure to assign it to yourself so that other maintainers don't start working on it at the same time.
|
||||
|
||||
## Landing Pull Requests
|
||||
|
||||
When landing pull requests, be sure to check the first line uses an appropriate commit message prefix (e.g. docs, feat, lib, etc). If there is more than one commit, try to squash into a single commit. Usually this can just be done with the GitHub UI when merging the PR. Use "Squash and merge". To help ensure that everyone in the community has an opportunity to review and comment on pull requests, it's often good to have some time after a pull request has been submitted, and before it has landed. Some guidelines here about approvals and timing.
|
||||
|
||||
* No pull request may land without passing all automated checks
|
||||
* All pull requests require at least one approval from a maintainer before landing
|
||||
* A pull request author may approve their own PR, but will need an additional approval to land it
|
||||
* If a maintainer has submitted a pull request and it has not received approval from at least one other maintainer, it can be landed after 72 hours
|
||||
* If a pull request has both approvals and requested changes, it can't be landed until those requested changes are resolved
|
||||
|
||||
## Branch Management
|
||||
|
||||
The `master` branch is the bleeding edge. New major versions of the module
|
||||
are cut from this branch and tagged. If you intend to submit a pull request
|
||||
you should use `master HEAD` as your starting point.
|
||||
|
||||
Each major release will result in a new branch and tag. For example, the
|
||||
release of version 1.0.0 of the module will result in a `v1.0.0` tag on the
|
||||
release commit, and a new branch `v1.x.y` for subsequent minor and patch
|
||||
level releases of that major version. However, development will continue
|
||||
apace on `master` for the next major version - e.g. 2.0.0. Version branches
|
||||
are only created for each major version. Minor and patch level releases
|
||||
are simply tagged.
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# Maintainer's Guide
|
||||
|
||||
## Tips
|
||||
|
||||
Here are a few tips for repository maintainers.
|
||||
|
||||
* Stay on top of your pull requests. PRs that languish for too long can become difficult to merge.
|
||||
* Work from your own fork. As you are making contributions to the project, you should be working from your own fork just as outside contributors do. This keeps the branches in github to a minimum and reduces unnecessary CI runs.
|
||||
* Try to proactively label issues and pull requests with labels
|
||||
* Actively review pull requests as they are submitted
|
||||
* Triage issues once in a while in order to keep the repository alive.
|
||||
* If some issues are stale for too long because they are no longer valid/relevant or because the discussion reached no significant action items to perform, close them and invite the users to reopen if they need it.
|
||||
* If some PRs are no longer valid due to conflicts, but the PR is still needed, ask the contributor to rebase their PR.
|
||||
* If some issues and PRs are still relevant, use labels to help organize tasks.
|
||||
* If you find an issue that you want to create a pull request for, be sure to assign it to yourself so that other maintainers don't start working on it at the same time.
|
||||
|
||||
## Landing Pull Requests
|
||||
|
||||
When landing pull requests, be sure to check the first line uses an appropriate commit message prefix (e.g. docs, feat, lib, etc). If there is more than one commit, try to squash into a single commit. Usually this can just be done with the GitHub UI when merging the PR. Use "Squash and merge". To help ensure that everyone in the community has an opportunity to review and comment on pull requests, it's often good to have some time after a pull request has been submitted, and before it has landed. Some guidelines here about approvals and timing.
|
||||
|
||||
* No pull request may land without passing all automated checks
|
||||
* All pull requests require at least one approval from a maintainer before landing
|
||||
* A pull request author may approve their own PR, but will need an additional approval to land it
|
||||
* If a maintainer has submitted a pull request and it has not received approval from at least one other maintainer, it can be landed after 72 hours
|
||||
* If a pull request has both approvals and requested changes, it can't be landed until those requested changes are resolved
|
||||
|
||||
## Branch Management
|
||||
|
||||
The `main` branch is the bleeding edge. New major versions of the module
|
||||
are cut from this branch and tagged. If you intend to submit a pull request
|
||||
you should use `main HEAD` as your starting point.
|
||||
|
||||
Each major release will result in a new branch and tag. For example, the
|
||||
release of version 1.0.0 of the module will result in a `v1.0.0` tag on the
|
||||
release commit, and a new branch `v1.x.y` for subsequent minor and patch
|
||||
level releases of that major version. However, development will continue
|
||||
apace on `main` for the next major version - e.g. 2.0.0. Version branches
|
||||
are only created for each major version. Minor and patch level releases
|
||||
are simply tagged.
|
22
README.md
22
README.md
|
@ -63,8 +63,10 @@ Javadocs are available on [javadoc.io](https://www.javadoc.io):
|
|||
|
||||
- [cloudevents-api](https://www.javadoc.io/doc/io.cloudevents/cloudevents-api)
|
||||
- [cloudevents-core](https://www.javadoc.io/doc/io.cloudevents/cloudevents-core)
|
||||
- [cloudevents-avro-compact](https://www.javadoc.io/doc/io.cloudevents/cloudevents-avro-compact)
|
||||
- [cloudevents-json-jackson](https://www.javadoc.io/doc/io.cloudevents/cloudevents-json-jackson)
|
||||
- [cloudevents-protobuf](https://www.javadoc.io/doc/io.cloudevents/cloudevents-protobuf)
|
||||
- [cloudevents-xml](https://www.javadoc.io/doc/io.cloudevents/cloudevents-xml)
|
||||
- [cloudevents-http-basic](https://www.javadoc.io/doc/io.cloudevents/cloudevents-http-basic)
|
||||
- [cloudevents-http-restful-ws](https://www.javadoc.io/doc/io.cloudevents/cloudevents-http-restful-ws)
|
||||
- [cloudevents-http-vertx](https://www.javadoc.io/doc/io.cloudevents/cloudevents-http-vertx)
|
||||
|
@ -97,13 +99,25 @@ You can check out the examples in the [examples](examples) directory.
|
|||
|
||||
Each SDK may have its own unique processes, tooling and guidelines, common
|
||||
governance related material can be found in the
|
||||
[CloudEvents `community`](https://github.com/cloudevents/spec/tree/master/community)
|
||||
[CloudEvents `community`](https://github.com/cloudevents/spec/tree/main/docs)
|
||||
directory. In particular, in there you will find information concerning how SDK
|
||||
projects are
|
||||
[managed](https://github.com/cloudevents/spec/blob/master/community/SDK-GOVERNANCE.md),
|
||||
[guidelines](https://github.com/cloudevents/spec/blob/master/community/SDK-maintainer-guidelines.md)
|
||||
[managed](https://github.com/cloudevents/spec/blob/main/docs/SDK-GOVERNANCE.md),
|
||||
[guidelines](https://github.com/cloudevents/spec/blob/main/docs/SDK-maintainer-guidelines.md)
|
||||
for how PR reviews and approval, and our
|
||||
[Code of Conduct](https://github.com/cloudevents/spec/blob/master/community/GOVERNANCE.md#additional-information)
|
||||
[Code of Conduct](https://github.com/cloudevents/spec/blob/main/docs/GOVERNANCE.md#additional-information)
|
||||
information.
|
||||
|
||||
If there is a security concern with one of the CloudEvents specifications, or
|
||||
with one of the project's SDKs, please send an email to
|
||||
[cncf-cloudevents-security@lists.cncf.io](mailto:cncf-cloudevents-security@lists.cncf.io).
|
||||
|
||||
## Additional SDK Resources
|
||||
|
||||
- [List of current active maintainers](MAINTAINERS.md)
|
||||
- [How to contribute to the project](CONTRIBUTING.md)
|
||||
- [SDK's License](LICENSE)
|
||||
- [SDK's Release process](RELEASING.md)
|
||||
- [SDK Maintainer's guide](MAINTAINERS_GUIDE.md)
|
||||
|
||||
[http4k]: https://www.http4k.org/guide/reference/cloud_events/
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# Release Process
|
||||
|
||||
The release process is automated with Github actions. In order to perform a release:
|
||||
|
||||
1. Check if main CI pass.
|
||||
1. Open the Github repository main page and go in the tab "Actions". Trigger the workflow "Bump version" and insert the new version to release. This will create a new release PR.
|
||||
1. Check the release PR, merge it and cleanup the created branch.
|
||||
1. Wait for the CI to complete the deploy of the modules to OSSRH.
|
||||
1. Using the Github UI, create a new release, specifying the release notes and the tag to use.
|
||||
1. Trigger again the workflow "Bump version" to bump versions back to a snapshot version.
|
||||
1. Check the snapshot release PR, merge it and cleanup the created branch.
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cloudevents-amqp-proton</artifactId>
|
||||
|
@ -14,7 +14,7 @@
|
|||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<protonj.version>0.33.7</protonj.version>
|
||||
<protonj.version>0.34.1</protonj.version>
|
||||
<jsr305.version>3.0.2</jsr305.version>
|
||||
<module-name>io.cloudevents.amqp.proton</module-name>
|
||||
</properties>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cloudevents-api</artifactId>
|
||||
|
|
|
@ -216,4 +216,15 @@ public class CloudEventRWException extends RuntimeException {
|
|||
cause
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* An exception for use where none of the other variants are
|
||||
* appropriate.
|
||||
*
|
||||
* @param msg A description error message.
|
||||
* @return a new {@link CloudEventRWException}
|
||||
*/
|
||||
public static CloudEventRWException newOther(String msg){
|
||||
return new CloudEventRWException(CloudEventRWExceptionKind.OTHER, msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cloudevents-benchmarks</artifactId>
|
||||
|
@ -141,7 +141,7 @@
|
|||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<version>3.1.3</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
|
@ -161,7 +161,7 @@
|
|||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<version>3.3.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cloudevents-bom</artifactId>
|
||||
|
@ -83,7 +83,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>io.cloudevents.sql</artifactId>
|
||||
<artifactId>cloudevents-sql</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cloudevents-core</artifactId>
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.util.*;
|
|||
/**
|
||||
* This extension embeds context from Distributed Tracing so that distributed systems can include traces that span an event-driven system.
|
||||
*
|
||||
* @see <a href="https://github.com/cloudevents/spec/blob/master/extensions/distributed-tracing.md">https://github.com/cloudevents/spec/blob/master/extensions/distributed-tracing.md</a>
|
||||
* @see <a href="https://github.com/cloudevents/spec/blob/main/extensions/distributed-tracing.md">https://github.com/cloudevents/spec/blob/main/extensions/distributed-tracing.md</a>
|
||||
*/
|
||||
public final class DistributedTracingExtension implements CloudEventExtension {
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2018-Present The CloudEvents Authors
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.cloudevents.core.format;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.CloudEventData;
|
||||
import io.cloudevents.rw.CloudEventDataMapper;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>A construct that aggregates a two-part identifier of file formats and format contents transmitted on the Internet.
|
||||
*
|
||||
* <p>The two parts of a {@code ContentType} are its <em>type</em> and a <em>subtype</em>; separated by a forward slash ({@code /}).
|
||||
*
|
||||
* <p>The constants enumerated by {@code ContentType} correspond <em>only</em> to the specialized formats supported by the Java™ SDK for CloudEvents.
|
||||
*
|
||||
* @see io.cloudevents.core.format.EventFormat
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public enum ContentType {
|
||||
|
||||
/**
|
||||
* Content type associated with the JSON event format
|
||||
*/
|
||||
JSON("application/cloudevents+json"),
|
||||
/**
|
||||
* The content type for transports sending cloudevents in the protocol buffer format.
|
||||
*/
|
||||
PROTO("application/cloudevents+protobuf"),
|
||||
/**
|
||||
* The content type for transports sending cloudevents in the compact Avro format.
|
||||
*/
|
||||
AVRO_COMPACT("application/cloudevents+avrocompact"),
|
||||
/**
|
||||
* The content type for transports sending cloudevents in XML format.
|
||||
*/
|
||||
XML("application/cloudevents+xml");
|
||||
|
||||
private String value;
|
||||
|
||||
private ContentType(String value) { this.value = value; }
|
||||
|
||||
/**
|
||||
* Return a string consisting of the slash-delimited ({@code /}) two-part identifier for this {@code enum} constant.
|
||||
*/
|
||||
public String value() { return value; }
|
||||
|
||||
/**
|
||||
* Return a string consisting of the slash-delimited ({@code /}) two-part identifier for this {@code enum} constant.
|
||||
*/
|
||||
@Override
|
||||
public String toString() { return value(); }
|
||||
|
||||
}
|
|
@ -124,6 +124,9 @@ public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<S
|
|||
return self;
|
||||
}
|
||||
|
||||
// @TODO - I think this method should be removed/deprecated
|
||||
// **Number** Is NOT a valid CE Context atrribute type.
|
||||
|
||||
public SELF withExtension(@Nonnull String key, @Nonnull Number value) {
|
||||
if (!isValidExtensionName(key)) {
|
||||
throw CloudEventRWException.newInvalidExtensionName(key);
|
||||
|
@ -132,6 +135,14 @@ public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<S
|
|||
return self;
|
||||
}
|
||||
|
||||
public SELF withExtension(@Nonnull String key, @Nonnull Integer value) {
|
||||
if (!isValidExtensionName(key)) {
|
||||
throw CloudEventRWException.newInvalidExtensionName(key);
|
||||
}
|
||||
this.extensions.put(key, value);
|
||||
return self;
|
||||
}
|
||||
|
||||
public SELF withExtension(@Nonnull String key, @Nonnull Boolean value) {
|
||||
if (!isValidExtensionName(key)) {
|
||||
throw CloudEventRWException.newInvalidExtensionName(key);
|
||||
|
@ -208,12 +219,16 @@ public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<S
|
|||
return new IllegalStateException("Attribute '" + attributeName + "' cannot be null");
|
||||
}
|
||||
|
||||
protected static IllegalStateException createEmptyAttributeException(String attributeName) {
|
||||
return new IllegalStateException("Attribute '" + attributeName + "' cannot be empty");
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the extension name as defined in CloudEvents spec.
|
||||
*
|
||||
* @param name the extension name
|
||||
* @return true if extension name is valid, false otherwise
|
||||
* @see <a href="https://github.com/cloudevents/spec/blob/master/spec.md#attribute-naming-convention">attribute-naming-convention</a>
|
||||
* @see <a href="https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#naming-conventions">attribute-naming-conventions</a>
|
||||
*/
|
||||
private static boolean isValidExtensionName(String name) {
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2018-Present The CloudEvents Authors
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package io.cloudevents.core.provider;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.validator.CloudEventValidator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* CloudEventValidatorProvider is a singleton class which loads and access CE Validator service providers on behalf of service clients.
|
||||
*/
|
||||
public class CloudEventValidatorProvider {
|
||||
|
||||
private static final CloudEventValidatorProvider cloudEventValidatorProvider = new CloudEventValidatorProvider();
|
||||
|
||||
private final Collection<CloudEventValidator> validators;
|
||||
|
||||
private CloudEventValidatorProvider() {
|
||||
final ServiceLoader<CloudEventValidator> loader = ServiceLoader.load(CloudEventValidator.class);
|
||||
this.validators = new ArrayList<>(2);
|
||||
for (CloudEventValidator cloudEventValidator : loader) {
|
||||
validators.add(cloudEventValidator);
|
||||
}
|
||||
}
|
||||
|
||||
public static CloudEventValidatorProvider getInstance() {
|
||||
return cloudEventValidatorProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* iterates through available Cloudevent validators.
|
||||
*
|
||||
* @param cloudEvent event to validate.
|
||||
*/
|
||||
public void validate(CloudEvent cloudEvent) {
|
||||
for (final CloudEventValidator validator : validators) {
|
||||
validator.validate(cloudEvent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import java.util.stream.StreamSupport;
|
|||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import io.cloudevents.core.format.ContentType;
|
||||
import io.cloudevents.core.format.EventFormat;
|
||||
import io.cloudevents.lang.Nullable;
|
||||
|
||||
|
@ -98,4 +99,14 @@ public final class EventFormatProvider {
|
|||
return this.formats.get(contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an event format starting from the content type.
|
||||
*
|
||||
* @param contentType the content type to resolve the event format
|
||||
* @return null if no format was found for the provided content type
|
||||
*/
|
||||
@Nullable
|
||||
public EventFormat resolveFormat(ContentType contentType) {
|
||||
return this.formats.get(contentType.value());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,12 @@
|
|||
*/
|
||||
package io.cloudevents.core.v03;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.SpecVersion;
|
||||
import io.cloudevents.core.CloudEventUtils;
|
||||
import io.cloudevents.core.impl.BaseCloudEventBuilder;
|
||||
import io.cloudevents.core.provider.CloudEventValidatorProvider;
|
||||
import io.cloudevents.core.v1.CloudEventV1;
|
||||
import io.cloudevents.rw.CloudEventContextReader;
|
||||
import io.cloudevents.rw.CloudEventContextWriter;
|
||||
import io.cloudevents.rw.CloudEventRWException;
|
||||
|
@ -121,8 +124,15 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBui
|
|||
if (type == null) {
|
||||
throw createMissingAttributeException("type");
|
||||
}
|
||||
if (subject != null && subject.isEmpty()) {
|
||||
throw createEmptyAttributeException(("subject"));
|
||||
}
|
||||
|
||||
return new CloudEventV03(id, source, type, time, schemaurl, datacontenttype, subject, this.data, this.extensions);
|
||||
CloudEventV03 cloudEvent = new CloudEventV03(id, source, type, time, schemaurl, datacontenttype, subject, this.data, this.extensions);
|
||||
final CloudEventValidatorProvider validator = CloudEventValidatorProvider.getInstance();
|
||||
validator.validate(cloudEvent);
|
||||
|
||||
return cloudEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,6 +21,8 @@ import io.cloudevents.CloudEvent;
|
|||
import io.cloudevents.SpecVersion;
|
||||
import io.cloudevents.core.CloudEventUtils;
|
||||
import io.cloudevents.core.impl.BaseCloudEventBuilder;
|
||||
import io.cloudevents.core.provider.CloudEventValidatorProvider;
|
||||
import io.cloudevents.core.validator.CloudEventValidator;
|
||||
import io.cloudevents.rw.CloudEventContextReader;
|
||||
import io.cloudevents.rw.CloudEventContextWriter;
|
||||
import io.cloudevents.rw.CloudEventRWException;
|
||||
|
@ -118,8 +120,16 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBui
|
|||
if (type == null) {
|
||||
throw createMissingAttributeException(TYPE);
|
||||
}
|
||||
if (subject != null && subject.isEmpty()) {
|
||||
throw createEmptyAttributeException(("subject"));
|
||||
}
|
||||
|
||||
return new CloudEventV1(id, source, type, datacontenttype, dataschema, subject, time, this.data, this.extensions);
|
||||
CloudEvent cloudEvent = new CloudEventV1(id, source, type, datacontenttype, dataschema, subject, time, this.data, this.extensions);
|
||||
|
||||
final CloudEventValidatorProvider validator = CloudEventValidatorProvider.getInstance();
|
||||
validator.validate(cloudEvent);
|
||||
|
||||
return cloudEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2018-Present The CloudEvents Authors
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package io.cloudevents.core.validator;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
|
||||
/**
|
||||
* @author Vinay Bhat
|
||||
* Interface which defines validation for CloudEvents attributes and extensions.
|
||||
*/
|
||||
public interface CloudEventValidator {
|
||||
|
||||
/**
|
||||
* Validate the attributes of a CloudEvent.
|
||||
*
|
||||
* @param cloudEvent the CloudEvent to validate
|
||||
*/
|
||||
void validate(CloudEvent cloudEvent);
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package io.cloudevents.core.data;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -8,7 +10,7 @@ class PojoCloudEventDataTest {
|
|||
|
||||
@Test
|
||||
void testWrapAndMemoization() {
|
||||
PojoCloudEventData<Integer> data = PojoCloudEventData.wrap(10, i -> i.toString().getBytes());
|
||||
PojoCloudEventData<Integer> data = PojoCloudEventData.wrap(10, i -> i.toString().getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
assertThat(data.getValue())
|
||||
.isEqualTo(10);
|
||||
|
@ -16,7 +18,7 @@ class PojoCloudEventDataTest {
|
|||
byte[] firstConversion = data.toBytes();
|
||||
|
||||
assertThat(firstConversion)
|
||||
.isEqualTo("10".getBytes());
|
||||
.isEqualTo("10".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
assertThat(data.toBytes())
|
||||
.isSameAs(firstConversion);
|
||||
|
@ -24,7 +26,7 @@ class PojoCloudEventDataTest {
|
|||
|
||||
@Test
|
||||
void testAlreadySerializedValue() {
|
||||
byte[] serialized = "10".getBytes();
|
||||
byte[] serialized = "10".getBytes(StandardCharsets.UTF_8);
|
||||
PojoCloudEventData<Integer> data = PojoCloudEventData.wrap(10, v -> serialized);
|
||||
|
||||
assertThat(data.getValue())
|
||||
|
|
|
@ -55,7 +55,7 @@ public class CSVFormat implements EventFormat {
|
|||
event.getData() != null
|
||||
? new String(Base64.getEncoder().encode(event.getData().toBytes()), StandardCharsets.UTF_8)
|
||||
: "null"
|
||||
).getBytes();
|
||||
).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,7 +70,7 @@ public class CSVFormat implements EventFormat {
|
|||
URI dataschema = splitted[5].equals("null") ? null : URI.create(splitted[5]);
|
||||
String subject = splitted[6].equals("null") ? null : splitted[6];
|
||||
OffsetDateTime time = splitted[7].equals("null") ? null : Time.parseTime(splitted[7]);
|
||||
byte[] data = splitted[8].equals("null") ? null : Base64.getDecoder().decode(splitted[8].getBytes());
|
||||
byte[] data = splitted[8].equals("null") ? null : Base64.getDecoder().decode(splitted[8].getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
CloudEventBuilder builder = CloudEventBuilder.fromSpecVersion(sv)
|
||||
.withId(id)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.cloudevents.core.mock;
|
||||
|
||||
import io.cloudevents.CloudEventData;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MyCloudEventData implements CloudEventData {
|
||||
|
@ -14,7 +14,7 @@ public class MyCloudEventData implements CloudEventData {
|
|||
|
||||
@Override
|
||||
public byte[] toBytes() {
|
||||
return Integer.toString(value).getBytes();
|
||||
return Integer.toString(value).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package io.cloudevents.core.test;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.validator.CloudEventValidator;
|
||||
|
||||
public class CloudEventCustomValidator implements CloudEventValidator {
|
||||
|
||||
@Override
|
||||
public void validate(CloudEvent cloudEvent) {
|
||||
String namespace = null;
|
||||
if ((namespace = (String) cloudEvent.getExtension("namespace")) != null &&
|
||||
!namespace.equals("sales")){
|
||||
throw new IllegalStateException("Expecting sales in namespace extension");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -23,6 +23,7 @@ import io.cloudevents.types.Time;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -39,9 +40,9 @@ public class Data {
|
|||
public static final String SUBJECT = "sub";
|
||||
public static final OffsetDateTime TIME = Time.parseTime("2018-04-26T14:48:09+02:00");
|
||||
|
||||
public static byte[] DATA_JSON_SERIALIZED = "{}".getBytes();
|
||||
public static byte[] DATA_XML_SERIALIZED = "<stuff></stuff>".getBytes();
|
||||
public static byte[] DATA_TEXT_SERIALIZED = "Hello World Lorena!".getBytes();
|
||||
public static byte[] DATA_JSON_SERIALIZED = "{}".getBytes(StandardCharsets.UTF_8);
|
||||
public static byte[] DATA_XML_SERIALIZED = "<stuff></stuff>".getBytes(StandardCharsets.UTF_8);
|
||||
public static byte[] DATA_TEXT_SERIALIZED = "Hello World Lorena!".getBytes(StandardCharsets.UTF_8);
|
||||
public static byte[] BINARY_VALUE = { (byte) 0xE0, (byte) 0xFF, (byte) 0x00, (byte) 0x44, (byte) 0xAA }; // Base64: 4P8ARKo=
|
||||
|
||||
public static final CloudEvent V1_MIN = CloudEventBuilder.v1()
|
||||
|
|
|
@ -158,4 +158,27 @@ public class CloudEventBuilderTest {
|
|||
).hasMessageContaining("Attribute 'type' cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMissingSubject() {
|
||||
CloudEvent actual = CloudEventBuilder
|
||||
.v03()
|
||||
.withId("000")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.withType(TYPE)
|
||||
.build();
|
||||
|
||||
assertThat(actual).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptySubject() {
|
||||
assertThatCode(() -> CloudEventBuilder
|
||||
.v03()
|
||||
.withId("000")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.withType(TYPE)
|
||||
.withSubject("")
|
||||
.build()
|
||||
).hasMessageContaining("Attribute 'subject' cannot be empty");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,4 +142,39 @@ public class CloudEventBuilderTest {
|
|||
).hasMessageContaining("Attribute 'type' cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidatorProvider(){
|
||||
assertThatCode(() -> CloudEventBuilder
|
||||
.v1()
|
||||
.withId("000")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.withType(TYPE)
|
||||
.withExtension("namespace", "order")
|
||||
.build()
|
||||
).hasMessageContaining("Expecting sales in namespace extension");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMissingSubject() {
|
||||
CloudEvent actual = CloudEventBuilder
|
||||
.v1()
|
||||
.withId("000")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.withType(TYPE)
|
||||
.build();
|
||||
|
||||
assertThat(actual).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptySubject() {
|
||||
assertThatCode(() -> CloudEventBuilder
|
||||
.v1()
|
||||
.withId("000")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.withType(TYPE)
|
||||
.withSubject("")
|
||||
.build()
|
||||
).hasMessageContaining("Attribute 'subject' cannot be empty");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
io.cloudevents.core.test.CloudEventCustomValidator
|
|
@ -1,14 +1,14 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.0.3.4)
|
||||
activesupport (6.0.6.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
zeitwerk (~> 2.2, >= 2.2.2)
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
|
@ -16,7 +16,7 @@ GEM
|
|||
colorator (1.1.0)
|
||||
commonmarker (0.17.13)
|
||||
ruby-enum (~> 0.5)
|
||||
concurrent-ruby (1.1.7)
|
||||
concurrent-ruby (1.2.0)
|
||||
dnsruby (1.61.5)
|
||||
simpleidn (~> 0.1)
|
||||
em-websocket (0.5.2)
|
||||
|
@ -82,7 +82,7 @@ GEM
|
|||
octokit (~> 4.0)
|
||||
public_suffix (~> 3.0)
|
||||
typhoeus (~> 1.3)
|
||||
html-pipeline (2.14.0)
|
||||
html-pipeline (2.14.3)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
http_parser.rb (0.6.0)
|
||||
|
@ -203,19 +203,19 @@ GEM
|
|||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.3)
|
||||
listen (3.3.0)
|
||||
listen (3.7.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.8.0)
|
||||
mini_portile2 (2.8.8)
|
||||
minima (2.5.1)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-feed (~> 0.9)
|
||||
jekyll-seo-tag (~> 2.1)
|
||||
minitest (5.14.2)
|
||||
minitest (5.17.0)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.13.6)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
nokogiri (1.18.3)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
octokit (4.19.0)
|
||||
faraday (>= 0.9)
|
||||
|
@ -223,12 +223,12 @@ GEM
|
|||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (3.1.1)
|
||||
racc (1.6.0)
|
||||
racc (1.8.1)
|
||||
rake (13.0.1)
|
||||
rb-fsevent (0.10.4)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.5)
|
||||
rexml (3.3.9)
|
||||
rouge (3.23.0)
|
||||
ruby-enum (0.8.0)
|
||||
i18n
|
||||
|
@ -250,13 +250,13 @@ GEM
|
|||
thread_safe (0.3.6)
|
||||
typhoeus (1.4.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (1.2.8)
|
||||
tzinfo (1.2.11)
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
zeitwerk (2.4.1)
|
||||
zeitwerk (2.6.6)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
|
|
@ -11,7 +11,7 @@ search_enabled: true
|
|||
gh_edit_link: true
|
||||
gh_edit_link_text: "Edit this page on GitHub."
|
||||
gh_edit_repository: "https://github.com/cloudevents/sdk-java"
|
||||
gh_edit_branch: "master"
|
||||
gh_edit_branch: "main"
|
||||
gh_edit_source: docs
|
||||
gh_edit_view_mode: "tree"
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ binding for CloudEvents:
|
|||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-amqp-proton</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -43,5 +43,5 @@ public class ProtonAmqpMessageFactory {
|
|||
The example uses the `vertx-proton` integration to send/receive CloudEvent
|
||||
messages over AMQP:
|
||||
|
||||
- [Vertx AmqpServer](https://github.com/cloudevents/sdk-java/tree/master/examples/amqp-proton/src/main/java/io/cloudevents/examples/amqp/vertx/AmqpServer.java)
|
||||
- [Vertx AmqpClient](https://github.com/cloudevents/sdk-java/tree/master/examples/amqp-proton/src/main/java/io/cloudevents/examples/amqp/vertx/AmqpClient.java)
|
||||
- [Vertx AmqpServer](https://github.com/cloudevents/sdk-java/tree/main/examples/amqp-proton/src/main/java/io/cloudevents/examples/amqp/vertx/AmqpServer.java)
|
||||
- [Vertx AmqpClient](https://github.com/cloudevents/sdk-java/tree/main/examples/amqp-proton/src/main/java/io/cloudevents/examples/amqp/vertx/AmqpClient.java)
|
||||
|
|
|
@ -17,7 +17,7 @@ For Maven based projects, use the following dependency:
|
|||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-api</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
title: CloudEvents Avro Compact
|
||||
nav_order: 4
|
||||
---
|
||||
|
||||
# CloudEvents Avro Compact
|
||||
|
||||
[](http://www.javadoc.io/doc/io.cloudevents/cloudevents-avro-compact)
|
||||
|
||||
This module provides the Avro Compact `EventFormat` implementation.
|
||||
|
||||
# Setup
|
||||
For Maven based projects, use the following dependency:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-avro-compact</artifactId>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
No further configuration is required is use the module.
|
||||
|
||||
## Using the Avro Compact Event Format
|
||||
|
||||
### Event serialization
|
||||
|
||||
```java
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.provider.EventFormatProvider;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
import io.cloudevents.avro.avro.compact.AvroCompactFormat;
|
||||
|
||||
CloudEvent event = CloudEventBuilder.v1()
|
||||
.withId("hello")
|
||||
.withType("example.vertx")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.build();
|
||||
|
||||
byte[] serialized = EventFormatProvider
|
||||
.getInstance()
|
||||
.resolveFormat(AvroCompactFormat.CONTENT_TYPE)
|
||||
.serialize(event);
|
||||
```
|
||||
|
||||
The `EventFormatProvider` will automatically resolve the format using the
|
||||
`ServiceLoader` APIs.
|
||||
|
|
@ -16,7 +16,7 @@ For Maven based projects, use the following dependency:
|
|||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-core</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -34,7 +34,7 @@ final CloudEvent event = CloudEventBuilder.v1()
|
|||
.withId("000")
|
||||
.withType("example.demo")
|
||||
.withSource(URI.create("http://example.com"))
|
||||
.withData("text/plain","Hello world!".getBytes())
|
||||
.withData("text/plain","Hello world!".getBytes("UTF-8"))
|
||||
.build();
|
||||
```
|
||||
|
||||
|
@ -87,7 +87,7 @@ with Jackson, add `cloudevents-json-jackson` as a dependency and then using the
|
|||
`EventFormatProvider`:
|
||||
|
||||
```java
|
||||
import io.cloudevents.core.format.EventFormatProvider;
|
||||
import io.cloudevents.core.provider.EventFormatProvider;
|
||||
import io.cloudevents.jackson.JsonFormat;
|
||||
|
||||
EventFormat format = EventFormatProvider
|
||||
|
@ -110,6 +110,6 @@ CloudEvent extensions can be materialized in their respective POJOs using the
|
|||
import io.cloudevents.core.extensions.DistributedTracingExtension;
|
||||
import io.cloudevents.core.provider.ExtensionProvider;
|
||||
|
||||
DistributedTracingExtension dte = ExtensionsParser.getInstance()
|
||||
DistributedTracingExtension dte = ExtensionProvider.getInstance()
|
||||
.parseExtension(DistributedTracingExtension.class, event);
|
||||
```
|
||||
|
|
|
@ -27,7 +27,7 @@ HTTP Transport:
|
|||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-http-basic</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -48,6 +48,6 @@ public class HttpMessageFactory {
|
|||
|
||||
## Examples
|
||||
|
||||
- [Standard Java HttpServer](https://github.com/cloudevents/sdk-java/tree/master/examples/basic-http/src/main/java/io/cloudevents/examples/http/basic/BasicHttpServer.java)
|
||||
- [Http Client with HttpURLConnection](https://github.com/cloudevents/sdk-java/tree/master/examples/basic-http/src/main/java/io/cloudevents/examples/http/basic/HttpURLConnectionClient.java)
|
||||
- [Http Servlet with Jetty](https://github.com/cloudevents/sdk-java/tree/master/examples/basic-http/src/main/java/io/cloudevents/examples/http/basic/JettyServer.java)
|
||||
- [Standard Java HttpServer](https://github.com/cloudevents/sdk-java/tree/main/examples/basic-http/src/main/java/io/cloudevents/examples/http/basic/BasicHttpServer.java)
|
||||
- [Http Client with HttpURLConnection](https://github.com/cloudevents/sdk-java/tree/main/examples/basic-http/src/main/java/io/cloudevents/examples/http/basic/HttpURLConnectionClient.java)
|
||||
- [Http Servlet with Jetty](https://github.com/cloudevents/sdk-java/tree/main/examples/basic-http/src/main/java/io/cloudevents/examples/http/basic/JettyServer.java)
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
---
|
||||
title: CloudEvents HTTP Jakarta EE 9+ - Jakarta RESTful Web Services
|
||||
nav_order: 5
|
||||
---
|
||||
|
||||
# HTTP Protocol Binding for Jakarta EE 9+ - Jakarta RESTful Web Services
|
||||
|
||||
[](https://www.javadoc.io/doc/io.cloudevents/cloudevents-http-restful-ws)
|
||||
|
||||
For Maven based projects, use the following to configure the CloudEvents Jakarta
|
||||
RESTful Web Services Binding for Jakarta EE 9+:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-http-restful-ws-jakarta</artifactId>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
This integration is tested with Jersey (Requires JDK11 or higher), RestEasy & Microprofile Liberty.
|
||||
|
||||
#### * Before using this package ensure your web framework does support the `jakarta.*` namespace.
|
||||
|
||||
## Receiving CloudEvents
|
||||
|
||||
You need to configure the `CloudEventsProvider` to enable
|
||||
marshalling/unmarshalling of CloudEvents.
|
||||
|
||||
Below is a sample on how to read and write CloudEvents:
|
||||
|
||||
```java
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
@Path("/")
|
||||
public class EventReceiverResource {
|
||||
|
||||
|
||||
|
||||
@GET
|
||||
@Path("getMinEvent")
|
||||
public CloudEvent getMinEvent() {
|
||||
return CloudEventBuilder.v1()
|
||||
.withId("hello")
|
||||
.withType("example.vertx")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.build();
|
||||
}
|
||||
|
||||
// Return the CloudEvent using the HTTP binding structured encoding
|
||||
@GET
|
||||
@Path("getStructuredEvent")
|
||||
@StructuredEncoding("application/cloudevents+csv")
|
||||
public CloudEvent getStructuredEvent() {
|
||||
return CloudEventBuilder.v1()
|
||||
.withId("hello")
|
||||
.withType("example.vertx")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("postEventWithoutBody")
|
||||
public Response postEvent(CloudEvent inputEvent) {
|
||||
// Handle the event
|
||||
return Response.ok().build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Sending CloudEvents
|
||||
|
||||
You need to configure the `CloudEventsProvider` to enable
|
||||
marshalling/unmarshalling of CloudEvents.
|
||||
|
||||
Below is a sample on how to use the client to send a CloudEvent:
|
||||
|
||||
```java
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.http.restful.ws.CloudEventsProvider;
|
||||
|
||||
import jakarta.ws.rs.client.Entity;
|
||||
import jakarta.ws.rs.client.WebTarget;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class CloudEventSender {
|
||||
|
||||
public Response sendEvent(WebTarget target, CloudEvent event) {
|
||||
return target
|
||||
.path("postEvent")
|
||||
.request()
|
||||
.buildPost(Entity.entity(event, CloudEventsProvider.CLOUDEVENT_TYPE))
|
||||
.invoke();
|
||||
}
|
||||
|
||||
public Response sendEventAsStructured(WebTarget target, CloudEvent event) {
|
||||
return target
|
||||
.path("postEvent")
|
||||
.request()
|
||||
.buildPost(Entity.entity(event, "application/cloudevents+json"))
|
||||
.invoke();
|
||||
}
|
||||
|
||||
public CloudEvent getEvent(WebTarget target) {
|
||||
Response response = target
|
||||
.path("getEvent")
|
||||
.request()
|
||||
.buildGet()
|
||||
.invoke();
|
||||
|
||||
return response.readEntity(CloudEvent.class);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Migrating EE 8 applications to EE 9+
|
||||
The main change between Jakarta EE 8 and Jakarta EE 9 and future versions is the changing of the `javax.` to `jakarta.` namespaces used by key packages such as `jakarta.ws.rs-api` which provides the restful-ws API.
|
||||
|
||||
This change largely impacts only `import` statements it does filter down to dependencies such as this.
|
||||
|
||||
### Application migration
|
||||
For application migration we would recommend reviewing materials available from https://jakarta.ee/resources/#documentation as a starting point.
|
||||
|
||||
### CloudEvents Dependency
|
||||
To migrate to use EE 9+ supported package - replace `cloudevents-http-restful-ws` with `cloudevents-http-restful-ws-jakarta` and ensure the version is a minimum of `2.5.0-SNAPSHOT`
|
||||
|
||||
## Examples
|
||||
|
||||
- [Microprofile and Liberty](https://github.com/cloudevents/sdk-java/tree/main/examples/restful-ws-micropofile-liberty)
|
||||
|
|
@ -3,18 +3,18 @@ title: CloudEvents HTTP Jakarta RESTful Web Services
|
|||
nav_order: 5
|
||||
---
|
||||
|
||||
# HTTP Protocol Binding for Jakarta RESTful Web Services
|
||||
# HTTP Protocol Binding for Jakarta EE8 - RESTful Web Services
|
||||
|
||||
[](http://www.javadoc.io/doc/io.cloudevents/cloudevents-http-restful-ws)
|
||||
|
||||
For Maven based projects, use the following to configure the CloudEvents Jakarta
|
||||
RESTful Web Services Binding:
|
||||
RESTful Web Services Binding for Jakarta EE 8:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-http-restful-ws</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -118,5 +118,5 @@ public class CloudEventSender {
|
|||
|
||||
## Examples
|
||||
|
||||
- [Quarkus and Resteasy](https://github.com/cloudevents/sdk-java/tree/master/examples/restful-ws-quarkus)
|
||||
- [Jersey and Spring Boot](https://github.com/cloudevents/sdk-java/tree/master/examples/restful-ws-spring-boot)
|
||||
- [Quarkus and Resteasy](https://github.com/cloudevents/sdk-java/tree/main/examples/restful-ws-quarkus)
|
||||
- [Jersey and Spring Boot](https://github.com/cloudevents/sdk-java/tree/main/examples/restful-ws-spring-boot)
|
||||
|
|
|
@ -14,7 +14,7 @@ HTTP Transport:
|
|||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-http-vertx</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -95,4 +95,4 @@ public class CloudEventClientVerticle extends AbstractVerticle {
|
|||
|
||||
## Examples:
|
||||
|
||||
- [Vert.x Client and Server](https://github.com/cloudevents/sdk-java/tree/master/examples/vertx)
|
||||
- [Vert.x Client and Server](https://github.com/cloudevents/sdk-java/tree/main/examples/vertx)
|
||||
|
|
|
@ -42,6 +42,8 @@ Using the Java SDK you can:
|
|||
| - [Jackson](json-jackson.md) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Protobuf Event Format | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| - [Proto](protobuf.md) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| XML Event Format | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| - [XML](xml.md) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| [Kafka Protocol Binding](kafka.md) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| MQTT Protocol Binding | :x: | :x: |
|
||||
| NATS Protocol Binding | :x: | :x: |
|
||||
|
@ -62,7 +64,8 @@ receive CloudEvents, check out the dedicated pages:
|
|||
|
||||
- [AMQP using Proton](amqp-proton.md)
|
||||
- [HTTP using Vert.x](http-vertx.md)
|
||||
- [HTTP using Jakarta Restful WS](http-jakarta-restful-ws.md)
|
||||
- [HTTP using Jakarta EE 8 - Jakarta Restful WS](http-jakarta-restful-ws.md)
|
||||
- [HTTP using Jakarta EE 9+ - Jakarta Restful WS](http-jakarta-restful-ws-jakarta.md)
|
||||
- [HTTP using Spring](spring.md)
|
||||
- [HTTP using Jackson](json-jackson.md)
|
||||
- [Kafka](kafka.md)
|
||||
|
@ -72,7 +75,7 @@ and related interfaces, in order to interoperate with the other components of
|
|||
the SDK, check out the [API module documentation](api.md).
|
||||
|
||||
You can also check out the
|
||||
[**Examples**](https://github.com/cloudevents/sdk-java/tree/master/examples).
|
||||
[**Examples**](https://github.com/cloudevents/sdk-java/tree/main/examples).
|
||||
|
||||
## Modules
|
||||
|
||||
|
@ -95,10 +98,13 @@ a different feature from the different sub specs of
|
|||
[Jackson](https://github.com/FasterXML/jackson)
|
||||
- [`cloudevents-protobuf`] Implementation of [Protobuf Event format] using code generated
|
||||
from the standard [protoc](https://github.com/protocolbuffers/protobuf) compiler.
|
||||
- [`cloudevents-xml`] Implementation of the XML Event Format.
|
||||
- [`cloudevents-http-vertx`] Implementation of [HTTP Protocol Binding] with
|
||||
[Vert.x Core](https://vertx.io/)
|
||||
- [`cloudevents-http-restful-ws`] Implementation of [HTTP Protocol Binding]
|
||||
for [Jakarta Restful WS](https://jakarta.ee/specifications/restful-ws/)
|
||||
for [Jakarta EE 8 Restful WS](https://jakarta.ee/specifications/restful-ws/2.1/)
|
||||
- [`cloudevents-http-restful-ws-jakarta`] Implementation of [HTTP Protocol Binding]
|
||||
for [Jakarta EE 9+ Restful WS](https://jakarta.ee/specifications/restful-ws/)
|
||||
- [`cloudevents-http-basic`] Generic implementation of [HTTP Protocol
|
||||
Binding], primarily intended for integrators
|
||||
- [`cloudevents-kafka`] Implementation of [Kafka Protocol Binding]
|
||||
|
@ -115,15 +121,17 @@ You can look at the latest published artifacts on
|
|||
[HTTP Protocol Binding]: https://github.com/cloudevents/spec/blob/v1.0/http-protocol-binding.md
|
||||
[Kafka Protocol Binding]: https://github.com/cloudevents/spec/blob/v1.0/kafka-protocol-binding.md
|
||||
[AMQP Protocol Binding]: https://github.com/cloudevents/spec/blob/v1.0/amqp-protocol-binding.md
|
||||
[`cloudevents-api`]: https://github.com/cloudevents/sdk-java/tree/master/api
|
||||
[`cloudevents-bom`]: https://github.com/cloudevents/sdk-java/tree/master/bom
|
||||
[`cloudevents-core`]: https://github.com/cloudevents/sdk-java/tree/master/core
|
||||
[`cloudevents-json-jackson`]: https://github.com/cloudevents/sdk-java/tree/master/formats/json-jackson
|
||||
[`cloudevents-protobuf`]: https://github.com/cloudevents/sdk-java/tree/master/formats/protobuf
|
||||
[`cloudevents-http-vertx`]: https://github.com/cloudevents/sdk-java/tree/master/http/vertx
|
||||
[`cloudevents-http-basic`]: https://github.com/cloudevents/sdk-java/tree/master/http/basic
|
||||
[`cloudevents-http-restful-ws`]: https://github.com/cloudevents/sdk-java/tree/master/http/restful-ws
|
||||
[`cloudevents-kafka`]: https://github.com/cloudevents/sdk-java/tree/master/kafka
|
||||
[`cloudevents-amqp-proton`]: https://github.com/cloudevents/sdk-java/tree/master/amqp
|
||||
[`cloudevents-spring`]: https://github.com/cloudevents/sdk-java/tree/master/spring
|
||||
[`cloudevents-api`]: https://github.com/cloudevents/sdk-java/tree/main/api
|
||||
[`cloudevents-bom`]: https://github.com/cloudevents/sdk-java/tree/main/bom
|
||||
[`cloudevents-core`]: https://github.com/cloudevents/sdk-java/tree/main/core
|
||||
[`cloudevents-json-jackson`]: https://github.com/cloudevents/sdk-java/tree/main/formats/json-jackson
|
||||
[`cloudevents-protobuf`]: https://github.com/cloudevents/sdk-java/tree/main/formats/protobuf
|
||||
[`cloudevents-xml`]: https://github.com/cloudevents/sdk-java/tree/main/formats/xml
|
||||
[`cloudevents-http-vertx`]: https://github.com/cloudevents/sdk-java/tree/main/http/vertx
|
||||
[`cloudevents-http-basic`]: https://github.com/cloudevents/sdk-java/tree/main/http/basic
|
||||
[`cloudevents-http-restful-ws`]: https://github.com/cloudevents/sdk-java/tree/main/http/restful-ws
|
||||
[`cloudevents-http-restful-ws-jakarta`]: https://github.com/cloudevents/sdk-java/tree/main/http/restful-ws-jakarta
|
||||
[`cloudevents-kafka`]: https://github.com/cloudevents/sdk-java/tree/main/kafka
|
||||
[`cloudevents-amqp-proton`]: https://github.com/cloudevents/sdk-java/tree/main/amqp
|
||||
[`cloudevents-spring`]: https://github.com/cloudevents/sdk-java/tree/main/spring
|
||||
[http4k]: https://www.http4k.org/guide/modules/cloud_events/
|
||||
|
|
|
@ -17,7 +17,7 @@ For Maven based projects, use the following dependency:
|
|||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-json-jackson</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -28,9 +28,9 @@ adding the dependency to your project:
|
|||
|
||||
```java
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.format.EventFormatProvider;
|
||||
import io.cloudevents.core.format.ContentType;
|
||||
import io.cloudevents.core.provider.EventFormatProvider;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
import io.cloudevents.jackson.JsonFormat;
|
||||
|
||||
CloudEvent event = CloudEventBuilder.v1()
|
||||
.withId("hello")
|
||||
|
@ -40,7 +40,7 @@ CloudEvent event = CloudEventBuilder.v1()
|
|||
|
||||
byte[]serialized = EventFormatProvider
|
||||
.getInstance()
|
||||
.resolveFormat(JsonFormat.CONTENT_TYPE)
|
||||
.resolveFormat(ContentType.JSON)
|
||||
.serialize(event);
|
||||
```
|
||||
|
||||
|
|
|
@ -10,14 +10,14 @@ nav_order: 5
|
|||
Implementation of Kafka Protocol Binding to send and receive CloudEvents.
|
||||
|
||||
For Maven based projects, use the following to configure the
|
||||
[Kafka Protocol Binding](https://github.com/cloudevents/spec/blob/master/kafka-protocol-binding.md):
|
||||
[Kafka Protocol Binding](https://github.com/cloudevents/spec/blob/main/kafka-protocol-binding.md):
|
||||
|
||||
```xml
|
||||
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-kafka</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -64,7 +64,7 @@ public class CloudEventProducer {
|
|||
You can configure the Encoding and EventFormat to use to emit the event.
|
||||
|
||||
Check out the
|
||||
[`CloudEventSerializer`](https://github.com/cloudevents/sdk-java/tree/master/kafka/src/main/java/io/cloudevents/kafka/CloudEventSerializer.java)
|
||||
[`CloudEventSerializer`](https://github.com/cloudevents/sdk-java/tree/main/kafka/src/main/java/io/cloudevents/kafka/CloudEventSerializer.java)
|
||||
javadoc for more info.
|
||||
|
||||
### Partition key extension
|
||||
|
@ -81,7 +81,7 @@ producerProps.put(
|
|||
|
||||
When using in your producer, this interceptor will pick the `partitionkey`
|
||||
extension from the event and will set it as record key, regardless of the input record key.
|
||||
Check out the [`PartitionKeyExtensionInterceptor`](https://github.com/cloudevents/sdk-java/tree/master/kafka/src/main/java/io/cloudevents/kafka/PartitionKeyExtensionInterceptor.java)
|
||||
Check out the [`PartitionKeyExtensionInterceptor`](https://github.com/cloudevents/sdk-java/tree/main/kafka/src/main/java/io/cloudevents/kafka/PartitionKeyExtensionInterceptor.java)
|
||||
javadoc for more info.
|
||||
|
||||
## Consuming CloudEvents
|
||||
|
|
|
@ -11,26 +11,28 @@ This module provides the Protocol Buffer (protobuf) `EventFormat` implementation
|
|||
Protobuf runtime and classes generated from the CloudEvents
|
||||
[proto spec](https://github.com/cloudevents/spec/blob/v1.0.1/spec.proto).
|
||||
|
||||
# Setup
|
||||
For Maven based projects, use the following dependency:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-protobuf</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
No further configuration is required is use the module.
|
||||
|
||||
## Using the Protobuf Event Format
|
||||
|
||||
You don't need to perform any operation to configure the module, more than
|
||||
adding the dependency to your project:
|
||||
### Event serialization
|
||||
|
||||
```java
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.format.EventFormatProvider;
|
||||
import io.cloudevents.core.format.ContentType;
|
||||
import io.cloudevents.core.provider.EventFormatProvider;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
import io.cloudevents.protobuf.ProtobufFormat;
|
||||
|
||||
CloudEvent event = CloudEventBuilder.v1()
|
||||
.withId("hello")
|
||||
|
@ -40,9 +42,55 @@ CloudEvent event = CloudEventBuilder.v1()
|
|||
|
||||
byte[]serialized = EventFormatProvider
|
||||
.getInstance()
|
||||
.resolveFormat(ProtobufFormat.CONTENT_TYPE)
|
||||
.resolveFormat(ContentType.PROTO)
|
||||
.serialize(event);
|
||||
```
|
||||
|
||||
The `EventFormatProvider` will resolve automatically the `ProtobufFormat` using the
|
||||
The `EventFormatProvider` will automatically resolve the `ProtobufFormat` using the
|
||||
`ServiceLoader` APIs.
|
||||
|
||||
## Passing Protobuf messages as CloudEvent data.
|
||||
|
||||
The `ProtoCloudEventData` capability provides a convenience mechanism to handle Protobuf message object data.
|
||||
|
||||
### Building
|
||||
|
||||
```java
|
||||
// Build my business event message.
|
||||
com.google.protobuf.Message myMessage = ..... ;
|
||||
|
||||
// Wrap the protobuf message as CloudEventData.
|
||||
CloudEventData ceData = ProtoCloudEventData.wrap(myMessage);
|
||||
|
||||
// Build the CloudEvent
|
||||
CloudEvent event = CloudEventBuilder.v1()
|
||||
.withId("hello")
|
||||
.withType("example.protodata")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.withData(ceData)
|
||||
.build();
|
||||
```
|
||||
|
||||
### Reading
|
||||
|
||||
If the `ProtobufFormat` is used to deserialize a CloudEvent that contains a protobuf message object as data you can use
|
||||
the `ProtoCloudEventData` to access it as an 'Any' directly.
|
||||
|
||||
```java
|
||||
|
||||
// Deserialize the event.
|
||||
CloudEvent myEvent = eventFormat.deserialize(raw);
|
||||
|
||||
// Get the Data
|
||||
CloudEventData eventData = myEvent.getData();
|
||||
|
||||
if (ceData instanceOf ProtoCloudEventData) {
|
||||
|
||||
// Obtain the protobuf 'any'
|
||||
Any anAny = ((ProtoCloudEventData) eventData).getAny();
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ For Maven based projects, use the following dependency:
|
|||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-spring</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -194,12 +194,12 @@ public Mono<CloudEvent> event(@RequestBody Mono<CloudEvent> body) {
|
|||
|
||||
Check out the integration tests and samples:
|
||||
|
||||
- [spring-reactive](https://github.com/cloudevents/sdk-java/tree/master/examples/spring-reactive)
|
||||
- [spring-reactive](https://github.com/cloudevents/sdk-java/tree/main/examples/spring-reactive)
|
||||
shows how to receive and send CloudEvents through HTTP using Spring Boot and
|
||||
Webflux.
|
||||
|
||||
- [spring-rsocket](https://github.com/cloudevents/sdk-java/tree/master/examples/spring-rsocket)
|
||||
- [spring-rsocket](https://github.com/cloudevents/sdk-java/tree/main/examples/spring-rsocket)
|
||||
shows how to receive and send CloudEvents through RSocket using Spring Boot.
|
||||
|
||||
- [spring-cloud-function](https://github.com/cloudevents/sdk-java/tree/master/examples/spring-function)
|
||||
- [spring-cloud-function](https://github.com/cloudevents/sdk-java/tree/main/examples/spring-function)
|
||||
shows how to consume and process CloudEvents via Spring Cloud Function.
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
---
|
||||
title: CloudEvents XML Format
|
||||
nav_order: 4
|
||||
---
|
||||
|
||||
# CloudEvents XML Format
|
||||
|
||||
[](http://www.javadoc.io/doc/io.cloudevents/cloudevents-xml)
|
||||
|
||||
This module provides and `EventFormat` implementation that adheres
|
||||
to the CloudEvent XML Format specification.
|
||||
|
||||
This format also supports specialized handling for XML CloudEvent `data`.
|
||||
|
||||
For Maven based projects, use the following dependency:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-xml</artifactId>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Using the XML Event Format
|
||||
|
||||
You don't need to perform any operation to configure the module, more than
|
||||
adding the dependency to your project:
|
||||
|
||||
```java
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.format.ContentType;
|
||||
import io.cloudevents.core.provider.EventFormatProvider;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
|
||||
CloudEvent event = CloudEventBuilder.v1()
|
||||
.withId("hello")
|
||||
.withType("example.xml")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.build();
|
||||
|
||||
byte[] serialized = EventFormatProvider
|
||||
.getInstance()
|
||||
.resolveFormat(ContentType.XML)
|
||||
.serialize(event);
|
||||
```
|
||||
|
||||
The `EventFormatProvider` will resolve automatically the `XMLFormat` using the
|
||||
`ServiceLoader` APIs.
|
||||
|
||||
XML Document data handling is supported via the `XMLCloudEventData`
|
||||
facility. This convenience wrapper can be used with `any` other supported
|
||||
format.
|
||||
|
||||
```java
|
||||
import org.w3c.dom.Document;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
import io.cloudevents.xml.XMLCloudEventData;
|
||||
|
||||
// Create the business event data.
|
||||
Document xmlDoc = .... ;
|
||||
|
||||
// Wrap it into CloudEventData
|
||||
CloudEventData myData = XMLCloudEventData.wrap(xmlDoc);
|
||||
|
||||
// Construct the event
|
||||
CloudEvent event = CloudEventBuilder.v1()
|
||||
.withId("hello")
|
||||
.withType("example.xml")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.withData(myData)
|
||||
.build();
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -3,14 +3,15 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloudevents-amqp-proton-example</artifactId>
|
||||
|
||||
<properties>
|
||||
<vertx.version>4.0.0.Beta1</vertx.version>
|
||||
<module-name>cloudevents.example.amqp.proton</module-name>
|
||||
<vertx.version>4.0.0.Beta1</vertx.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.apache.qpid.proton.message.Message;
|
|||
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* A example vertx-based AMQP client that interacts with a remote AMQP server to send and receive CloudEvent messages.
|
||||
|
@ -71,7 +72,7 @@ public class AmqpClient {
|
|||
.withSource(URI.create("http://127.0.0.1/amqp-client"))
|
||||
.withType("com.example.sampletype1")
|
||||
.withTime(Time.parseTime("2020-11-06T21:47:12.037467+00:00"))
|
||||
.withData(payload.toString().getBytes())
|
||||
.withData(payload.toString().getBytes(StandardCharsets.UTF_8))
|
||||
.build();
|
||||
|
||||
final Message message = ProtonAmqpMessageFactory.createWriter().writeBinary(event);
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.cloudevents.examples.amqp.vertx;
|
|||
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -81,7 +82,7 @@ public class AmqpServer {
|
|||
.withType("com.example.sampletype1")
|
||||
.withSource(URI.create("http://127.0.0.1/amqp-server"))
|
||||
.withTime(OffsetDateTime.now())
|
||||
.withData("{\"temp\": 5}".getBytes())
|
||||
.withData("{\"temp\": 5}".getBytes(StandardCharsets.UTF_8))
|
||||
.build();
|
||||
|
||||
final Message message = writer.writeBinary(event);
|
||||
|
|
|
@ -21,11 +21,14 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloudevents-basic-http-example</artifactId>
|
||||
<properties>
|
||||
<module-name>cloudevents.example.basic.http</module-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
@ -41,7 +44,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>9.4.41.v20210516</version>
|
||||
<version>9.4.55.v20240627</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -5,11 +5,14 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloudevents-kafka-example</artifactId>
|
||||
<properties>
|
||||
<module-name>cloudevents.example.kafka</module-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.apache.kafka.clients.producer.RecordMetadata;
|
|||
import org.apache.kafka.common.serialization.StringSerializer;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Properties;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -42,7 +43,7 @@ public class SampleProducer {
|
|||
|
||||
// Create an event template to set basic CloudEvent attributes
|
||||
CloudEventBuilder eventTemplate = CloudEventBuilder.v1()
|
||||
.withSource(URI.create("https://github.com/cloudevents/sdk-java/tree/master/examples/kafka"))
|
||||
.withSource(URI.create("https://github.com/cloudevents/sdk-java/tree/main/examples/kafka"))
|
||||
.withType("producer.example");
|
||||
|
||||
for (int i = 0; i < MESSAGE_COUNT; i++) {
|
||||
|
@ -53,7 +54,7 @@ public class SampleProducer {
|
|||
// Create the event starting from the template
|
||||
CloudEvent event = eventTemplate.newBuilder()
|
||||
.withId(id)
|
||||
.withData("text/plain", data.getBytes())
|
||||
.withData("text/plain", data.getBytes(StandardCharsets.UTF_8))
|
||||
.build();
|
||||
|
||||
// Send the record
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
|||
<modules>
|
||||
<module>kafka</module>
|
||||
<module>restful-ws-quarkus</module>
|
||||
<module>restful-ws-microprofile-liberty</module>
|
||||
<module>vertx</module>
|
||||
<module>basic-http</module>
|
||||
<module>restful-ws-spring-boot</module>
|
||||
|
@ -28,6 +29,7 @@
|
|||
<module>spring-reactive</module>
|
||||
<module>spring-rsocket</module>
|
||||
<module>spring-function</module>
|
||||
<module>rocketmq</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
# Cloudevents Restful WS Microprofile Example
|
||||
|
||||
This project uses Microprofile 5.0 with OpenLiberty
|
||||
|
||||
If you would like to know more about Microprofile go to https://microprofile.io
|
||||
|
||||
This Example uses Jakarta EE9 features as such the top level namespace of the `ws-api` packages has changed from `javax` to `jakarta` and uses the `cloudevents-http-restful-ws-jakarta` artifact.
|
||||
|
||||
## Build and Execution
|
||||
|
||||
### Running the application in dev mode
|
||||
|
||||
You can run your application in dev mode that enables live coding using:
|
||||
```
|
||||
mvn liberty:dev
|
||||
```
|
||||
|
||||
### Packaging and running the application
|
||||
|
||||
To Package and run as a minimum jar without a full OpenLiberty server
|
||||
```
|
||||
mvn liberty:package -Drunnable=mvn liberty:package -Dinclude=runnable
|
||||
```
|
||||
|
||||
### Making requests against the server
|
||||
|
||||
This sample application has a `/events` RESTful endpoint on the application `cloudevents-restful-ws-microprofile-example
|
||||
the base application is available at `http://localhost:9080/cloudevents-restful-ws-microprofile-example/`
|
||||
|
||||
There are three operations that can be performed:
|
||||
#### GET /events
|
||||
Returns a Cloud event with a payload containing a message
|
||||
|
||||
```shell
|
||||
curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events
|
||||
|
||||
* Trying 127.0.0.1:9080...
|
||||
* Connected to localhost (127.0.0.1) port 9080 (#0)
|
||||
> GET /cloudevents-restful-ws-microprofile-example/events HTTP/1.1
|
||||
> Host: localhost:9080
|
||||
> User-Agent: curl/7.83.1
|
||||
> Accept: */*
|
||||
>
|
||||
* Mark bundle as not supporting multiuse
|
||||
< HTTP/1.1 201 no-content
|
||||
< Ce-Id: hello
|
||||
< Ce-Source: http://localhost
|
||||
< Ce-Specversion: 1.0
|
||||
< Ce-Type: example.http
|
||||
< Content-Type: application/json
|
||||
< Content-Language: en-GB
|
||||
< Content-Length: 64
|
||||
< Date: Wed, 17 Aug 2022 14:01:50 GMT
|
||||
<
|
||||
{"message":"Welcome to this Cloudevents + Microprofile example"}
|
||||
```
|
||||
|
||||
#### POST /events
|
||||
POST a Cloudevent with a payload that is printed out in the server logs
|
||||
|
||||
```shell
|
||||
curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events \
|
||||
-H "Ce-Specversion: 1.0" \
|
||||
-H "Ce-Type: User" \
|
||||
-H "Ce-Source: io.cloudevents.examples/user" \
|
||||
-H "Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Ce-Subject: SUBJ-0001" \
|
||||
-d "hello"
|
||||
|
||||
* Trying 127.0.0.1:9080...
|
||||
* Connected to localhost (127.0.0.1) port 9080 (#0)
|
||||
> POST /cloudevents-restful-ws-microprofile-example/events HTTP/1.1
|
||||
> Host: localhost:9080
|
||||
> User-Agent: curl/7.83.1
|
||||
> Accept: */*
|
||||
> Ce-Specversion: 1.0
|
||||
> Ce-Type: User
|
||||
> Ce-Source: io.cloudevents.examples/user
|
||||
> Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78
|
||||
> Content-Type: application/json
|
||||
> Ce-Subject: SUBJ-0001
|
||||
> Content-Length: 5
|
||||
>
|
||||
* Mark bundle as not supporting multiuse
|
||||
< HTTP/1.1 204 No Content
|
||||
< Content-Language: en-GB
|
||||
< Content-Length: 0
|
||||
< Date: Thu, 18 Aug 2022 13:33:03 GMT
|
||||
<
|
||||
* Connection #0 to host localhost left intact
|
||||
```
|
||||
Server log statement
|
||||
```
|
||||
[INFO] Received request providing a event with body hello
|
||||
[INFO] CloudEvent{id='536808d3-88be-4077-9d7a-a3f162705f78', source=io.cloudevents.examples/user, type='User', datacontenttype='application/json', subject='SUBJ-0001', data=BytesCloudEventData{value=[104, 101, 108, 108, 111]}, extensions={}}
|
||||
```
|
||||
|
||||
#### POST /events/echo
|
||||
POST a Cloudevent with a payload and have it echoed back as a Cloudevent
|
||||
|
||||
```shell
|
||||
curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events/echo \
|
||||
-H "Ce-Specversion: 1.0" \
|
||||
-H "Ce-Type: User" \
|
||||
-H "Ce-Source: io.cloudevents.examples/user" \
|
||||
-H "Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Ce-Subject: SUBJ-0001" \
|
||||
-d "hello"
|
||||
|
||||
* Trying 127.0.0.1:9080...
|
||||
* Connected to localhost (127.0.0.1) port 9080 (#0)
|
||||
> POST /cloudevents-restful-ws-microprofile-example/rest/events/echo HTTP/1.1
|
||||
> Host: localhost:9080
|
||||
> User-Agent: curl/7.83.1
|
||||
> Accept: */*
|
||||
> Ce-Specversion: 1.0
|
||||
> Ce-Type: User
|
||||
> Ce-Source: io.cloudevents.examples/user
|
||||
> Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78
|
||||
> Content-Type: application/json
|
||||
> Ce-Subject: SUBJ-0001
|
||||
> Content-Length: 5
|
||||
>
|
||||
* Mark bundle as not supporting multiuse
|
||||
< HTTP/1.1 200 OK
|
||||
< Ce-Id: echo
|
||||
< Ce-Source: http://localhost
|
||||
< Ce-Specversion: 1.0
|
||||
< Ce-Type: echo.http
|
||||
< Content-Type: application/json
|
||||
< Content-Language: en-GB
|
||||
< Content-Length: 17
|
||||
< Date: Wed, 17 Aug 2022 12:57:59 GMT
|
||||
<
|
||||
{"echo": "hello"}* Connection #0 to host localhost left intact
|
||||
```
|
||||
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cloudevents-restful-ws-microprofile-liberty-example</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<properties>
|
||||
<openliberty.maven.version>3.5.1</openliberty.maven.version>
|
||||
<app.name>cloudevents-microprofile</app.name>
|
||||
<module-name>cloudevents.example.restful.ws.microprofile.liberty</module-name>
|
||||
|
||||
<!-- Liberty server properties -->
|
||||
<final.name>cloudeventsServer</final.name>
|
||||
<testServerHttpPort>9080</testServerHttpPort>
|
||||
<testServerHttpsPort>9443</testServerHttpsPort>
|
||||
<!--This is set in the ibm-web-ext.xml file -->
|
||||
<warContext>cloudeventsexperiments</warContext>
|
||||
<package.file>${project.build.directory}/${app.name}.zip</package.file>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>runnable</id>
|
||||
<properties>
|
||||
<package.file>${project.build.directory}/${app.name}.jar</package.file>
|
||||
<packaging.type>runnable</packaging.type>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.microprofile</groupId>
|
||||
<artifactId>microprofile</artifactId>
|
||||
<version>5.0</version>
|
||||
<type>pom</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-http-restful-ws-jakarta</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>3.6.11</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
<packagingExcludes>pom.xml</packagingExcludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.10</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-server-files</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeArtifactIds>server-snippet</includeArtifactIds>
|
||||
<prependGroupId>true</prependGroupId>
|
||||
<outputDirectory>
|
||||
${project.build.directory}/wlp/usr/servers/${wlpServerName}/configDropins/defaults
|
||||
</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-app</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/wlp/usr/servers/${wlpServerName}/dropins
|
||||
</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<includes>
|
||||
<include>${project.artifactId}-${project.version}.war</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Enable liberty-maven plugin -->
|
||||
<plugin>
|
||||
<groupId>io.openliberty.tools</groupId>
|
||||
<artifactId>liberty-maven-plugin</artifactId>
|
||||
<version>${openliberty.maven.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>package-server</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>create</goal>
|
||||
<goal>install-feature</goal>
|
||||
<goal>deploy</goal>
|
||||
<goal>package</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/wlp-package</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<include>runnable</include>
|
||||
<serverName>${final.name}</serverName>
|
||||
<bootstrapProperties>
|
||||
<project.name>${final.name}</project.name>
|
||||
<jwt.issuer>https://server.example.com</jwt.issuer>
|
||||
<app.context.root>/</app.context.root>
|
||||
</bootstrapProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Plugin to run functional tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.18.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>integration-test</phase>
|
||||
<id>integration-test</id>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/it/**</include>
|
||||
</includes>
|
||||
<systemPropertyVariables>
|
||||
<liberty.test.port>${testServerHttpPort}</liberty.test.port>
|
||||
<war.name>${warContext}</war.name>
|
||||
<running.bluemix>${running.bluemix}</running.bluemix>
|
||||
<cf.context.root>${cf.context.root}</cf.context.root>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>verify-results</id>
|
||||
<goals>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<summaryFile>${project.build.directory}/test-reports/it/failsafe-summary.xml</summaryFile>
|
||||
<reportsDirectory>${project.build.directory}/test-reports/it</reportsDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,9 @@
|
|||
package io.cloudevents.examples.microprofile;
|
||||
|
||||
import jakarta.ws.rs.ApplicationPath;
|
||||
import jakarta.ws.rs.core.Application;
|
||||
|
||||
@ApplicationPath("/")
|
||||
public class CloudEventsApplication extends Application {
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package io.cloudevents.examples.microprofile;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
import jakarta.enterprise.context.RequestScoped;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@RequestScoped
|
||||
@Path("events")
|
||||
public class CloudEventsJaxrsService {
|
||||
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public CloudEvent retrieveEvent(){
|
||||
System.out.println("Received request for an event");
|
||||
return CloudEventBuilder.v1()
|
||||
.withData("{\"message\":\"Welcome to this Cloudevents + Microprofile example\"}".getBytes(StandardCharsets.UTF_8))
|
||||
.withDataContentType("application/json")
|
||||
.withId("hello")
|
||||
.withType("example.http")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.build();
|
||||
}
|
||||
|
||||
//Example Curl - curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events -H "Ce-Specversion: 1.0" -H "Ce-Type: User" -H "Ce-Source: io.cloudevents.examples/user" -H "Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78" -H "Content-Type: application/json" -H "Ce-Subject: SUBJ-0001" -d "hello"
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response postEvent(CloudEvent event){
|
||||
System.out.println("Received request providing a event with body "+ new String(event.getData().toBytes(), StandardCharsets.UTF_8));
|
||||
System.out.println(event);
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
//Example Curl - curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events/echo -H "Ce-Specversion: 1.0" -H "Ce-Type: User" -H "Ce-Source: io.cloudevents.examples/user" -H "Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78" -H "Content-Type: application/json" -H "Ce-Subject: SUBJ-0001" -d "hello"
|
||||
|
||||
@POST
|
||||
@Path("/echo")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public CloudEvent echo(CloudEvent event){
|
||||
return CloudEventBuilder.v1()
|
||||
.withData("application/json", ("{\"echo\": \"" + new String(event.getData().toBytes(),StandardCharsets.UTF_8) + "\"}").getBytes(StandardCharsets.UTF_8))
|
||||
.withId("echo")
|
||||
.withType("echo.http")
|
||||
.withSource(URI.create("http://localhost"))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<server description="${project.name}">
|
||||
|
||||
<featureManager>
|
||||
<feature>microProfile-5.0</feature>
|
||||
</featureManager>
|
||||
|
||||
|
||||
<httpEndpoint id="defaultHttpEndpoint"
|
||||
httpPort="9080"
|
||||
httpsPort="9443"/>
|
||||
|
||||
<webApplication location="${project.name}.war">
|
||||
<classloader apiTypeVisibility="+third-party" />
|
||||
</webApplication>
|
||||
<mpMetrics authentication="false"/>
|
||||
|
||||
<!-- This is the keystore that will be used by SSL and by JWT. -->
|
||||
<keyStore id="defaultKeyStore" location="public.jks" type="JKS" password="atbash" />
|
||||
|
||||
|
||||
<!-- The MP JWT configuration that injects the caller's JWT into a ResourceScoped bean for inspection. -->
|
||||
<mpJwt id="jwtUserConsumer" keyName="theKeyId" audiences="targetService" issuer="${jwt.issuer}"/>
|
||||
|
||||
</server>
|
|
@ -1,7 +1,11 @@
|
|||
# Cloudevents Restful WS Quarkus example
|
||||
|
||||
This sample application has a `/users` REST endpoint in which you can manage the different users.
|
||||
The way to create users is through CloudEvents. Here is an example POST:
|
||||
The way to create users is through CloudEvents.
|
||||
|
||||
## Example requests
|
||||
|
||||
### [Binary Content mode](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/bindings/http-protocol-binding.md#31-binary-content-mode)
|
||||
|
||||
```shell script
|
||||
curl -v http://localhost:8080/users \
|
||||
|
@ -30,6 +34,29 @@ curl -v http://localhost:8080/users \
|
|||
< Location: http://localhost:8080/users
|
||||
```
|
||||
|
||||
### [Structured Content mode](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/bindings/http-protocol-binding.md#32-structured-content-mode)
|
||||
|
||||
```shell script
|
||||
curl -v http://localhost:8080/users \
|
||||
-H "Content-Type: application/cloudevents+json" \
|
||||
-d @examples/user_structured.json
|
||||
|
||||
> POST /users HTTP/1.1
|
||||
> Host: localhost:8080
|
||||
> User-Agent: curl/7.82.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/cloudevents+json
|
||||
> Content-Length: 290
|
||||
>
|
||||
|
||||
< HTTP/1.1 201 Created
|
||||
< Location: http://localhost:8081/users
|
||||
< content-length: 0
|
||||
<
|
||||
```
|
||||
|
||||
### Generated events
|
||||
|
||||
In order to also show how to create and send CloudEvents, generated events will be periodically sent
|
||||
each 2 seconds through HTTP to the same endpoint using a REST client.
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"specversion" : "1.0",
|
||||
"type" : "User",
|
||||
"source": "io.cloudevents.examples/user",
|
||||
"id": "536808d3-88be-4077-9d7a-a3f162705f78",
|
||||
"subject": "SUBJ-0001",
|
||||
"data" : {
|
||||
"username": "jsmith",
|
||||
"firstName": "John",
|
||||
"lastName": "Smith",
|
||||
"age": 37
|
||||
}
|
||||
}
|
|
@ -5,11 +5,12 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cloudevents-restful-ws-quarkus-example</artifactId>
|
||||
<properties>
|
||||
<module-name>cloudevents.example.restful.ws.quarkus</module-name>
|
||||
<quarkus-plugin.version>1.10.3.Final</quarkus-plugin.version>
|
||||
<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
|
||||
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package io.cloudevents.examples.quarkus.client;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.jackson.JsonFormat;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@Path("/users")
|
||||
|
@ -13,8 +14,11 @@ import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
|||
public interface UserClient {
|
||||
|
||||
// This will emit binary encoded events.
|
||||
// To use structured JSON encoding use @Produces(JsonFormat.CONTENT_TYPE).
|
||||
// To use structured JSON encoding use @Consumes(JsonFormat.CONTENT_TYPE).
|
||||
@POST
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
void emit(CloudEvent event);
|
||||
void emitBinary(CloudEvent event);
|
||||
|
||||
@POST
|
||||
@Consumes(JsonFormat.CONTENT_TYPE)
|
||||
void emitStructured(CloudEvent event);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import io.cloudevents.core.builder.CloudEventBuilder;
|
|||
import io.cloudevents.core.data.PojoCloudEventData;
|
||||
import io.cloudevents.examples.quarkus.model.User;
|
||||
import io.quarkus.scheduler.Scheduled;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
|
||||
@ApplicationScoped
|
||||
public class UserEventsGenerator {
|
||||
|
@ -38,8 +37,13 @@ public class UserEventsGenerator {
|
|||
@Scheduled(every="2s")
|
||||
public void init() {
|
||||
CloudEvent event = createEvent(userCount++);
|
||||
LOGGER.info("try to emit user: {}", event.getId());
|
||||
userClient.emit(event);
|
||||
if(userCount % 2 == 0) {
|
||||
LOGGER.info("try to emit binary event for user: {}", event.getId());
|
||||
userClient.emitBinary(event);
|
||||
} else {
|
||||
LOGGER.info("try to emit structured event for user: {}", event.getId());
|
||||
userClient.emitStructured(event);
|
||||
}
|
||||
}
|
||||
|
||||
private CloudEvent createEvent(long id) {
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.Map;
|
|||
|
||||
@Path("/users")
|
||||
@Consumes({MediaType.APPLICATION_JSON, JsonFormat.CONTENT_TYPE})
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public class UserResource {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);
|
||||
|
|
|
@ -4,7 +4,6 @@ import io.quarkus.test.junit.QuarkusTest;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
@QuarkusTest
|
||||
public class UserResourceTest {
|
||||
|
|
|
@ -11,11 +11,11 @@ mvn spring-boot:run
|
|||
You can try sending a request using `curl`:
|
||||
|
||||
```shell
|
||||
curl -X POST -v -d '{"username": "slinkydeveloper", "firstName": "Francesco", "lastName": "Guardiani", "age": 23}' \ ~
|
||||
-H'Content-type: application/json' \
|
||||
-H'Ce-id: 1' \
|
||||
-H'Ce-source: cloud-event-example' \
|
||||
-H'Ce-type: happybirthday.myapplication' \
|
||||
-H'Ce-specversion: 1.0' \
|
||||
curl -X POST -v -d '{"username": "slinkydeveloper", "firstName": "Francesco", "lastName": "Guardiani", "age": 23}' \
|
||||
-H 'Content-type: application/json' \
|
||||
-H 'Ce-id: 1' \
|
||||
-H 'Ce-source: cloud-event-example' \
|
||||
-H 'Ce-type: happybirthday.myapplication' \
|
||||
-H 'Ce-specversion: 1.0' \
|
||||
http://localhost:8080/happy_birthday
|
||||
```
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloudevents-spring-boot-example</artifactId>
|
||||
|
||||
<properties>
|
||||
<module-name>cloudevents.example.spring.boot</module-name>
|
||||
<spring-boot.version>2.3.2.RELEASE</spring-boot.version>
|
||||
<spring.version>5.2.9.RELEASE</spring.version>
|
||||
</properties>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# RocketMQ + CloudEvents Sample
|
||||
|
||||
This example demonstrates the integration of [RocketMQ 5.x client library](https://github.com/apache/rocketmq-clients)
|
||||
with CloudEvents to create a RocketMQ binding.
|
||||
|
||||
## Building the Project
|
||||
|
||||
```shell
|
||||
mvn package
|
||||
```
|
||||
|
||||
## Setting Up a RocketMQ Instance
|
||||
|
||||
Follow the [quickstart guide](https://rocketmq.apache.org/docs/quick-start/01quickstart) on the official RocketMQ
|
||||
website to set up the necessary components, including nameserver, proxy, and broker.
|
||||
|
||||
## Event Production
|
||||
|
||||
```shell
|
||||
mvn exec:java -Dexec.mainClass="io.cloudevents.examples.rocketmq.RocketmqProducer" -Dexec.args="foobar:8081 sample-topic"
|
||||
```
|
||||
|
||||
## Event Consumption
|
||||
|
||||
```shell
|
||||
mvn exec:java -Dexec.mainClass="io.cloudevents.examples.rocketmq.RocketmqConsumer" -Dexec.args="foobar:8081 sample-topic sample-consumer-group"
|
||||
```
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloudevents-rocketmq-example</artifactId>
|
||||
<properties>
|
||||
<module-name>cloudevents.example.rocketmq</module-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-rocketmq</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,50 @@
|
|||
package io.cloudevents.examples.rocketmq;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.message.MessageReader;
|
||||
import io.cloudevents.rocketmq.RocketMqMessageFactory;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import org.apache.rocketmq.client.apis.ClientConfiguration;
|
||||
import org.apache.rocketmq.client.apis.ClientException;
|
||||
import org.apache.rocketmq.client.apis.ClientServiceProvider;
|
||||
import org.apache.rocketmq.client.apis.consumer.ConsumeResult;
|
||||
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
|
||||
import org.apache.rocketmq.client.apis.consumer.PushConsumer;
|
||||
|
||||
public class RocketmqConsumer {
|
||||
private RocketmqConsumer() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException, ClientException, IOException {
|
||||
if (args.length < 3) {
|
||||
System.out.println("Usage: rocketmq_consumer <endpoints> <topic> <consumer_group>");
|
||||
return;
|
||||
}
|
||||
final ClientServiceProvider provider = ClientServiceProvider.loadService();
|
||||
String endpoints = args[0];
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.newBuilder()
|
||||
.setEndpoints(endpoints)
|
||||
.build();
|
||||
FilterExpression filterExpression = new FilterExpression();
|
||||
String topic = args[1];
|
||||
String consumerGroup = args[2];
|
||||
|
||||
// Create the RocketMQ Consumer.
|
||||
PushConsumer pushConsumer = provider.newPushConsumerBuilder()
|
||||
.setClientConfiguration(clientConfiguration)
|
||||
.setConsumerGroup(consumerGroup)
|
||||
.setSubscriptionExpressions(Collections.singletonMap(topic, filterExpression))
|
||||
.setMessageListener(messageView -> {
|
||||
final MessageReader reader = RocketMqMessageFactory.createReader(messageView);
|
||||
final CloudEvent event = reader.toEvent();
|
||||
System.out.println("Received event=" + event + ", messageId=" + messageView.getMessageId());
|
||||
return ConsumeResult.SUCCESS;
|
||||
})
|
||||
.build();
|
||||
// Block the main thread, no need for production environment.
|
||||
Thread.sleep(Long.MAX_VALUE);
|
||||
// Close the push consumer when you don't need it anymore.
|
||||
pushConsumer.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package io.cloudevents.examples.rocketmq;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.v1.CloudEventBuilder;
|
||||
import io.cloudevents.rocketmq.RocketMqMessageFactory;
|
||||
import io.cloudevents.types.Time;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.rocketmq.client.apis.ClientConfiguration;
|
||||
import org.apache.rocketmq.client.apis.ClientException;
|
||||
import org.apache.rocketmq.client.apis.ClientServiceProvider;
|
||||
import org.apache.rocketmq.client.apis.message.Message;
|
||||
import org.apache.rocketmq.client.apis.producer.Producer;
|
||||
import org.apache.rocketmq.client.apis.producer.SendReceipt;
|
||||
import org.apache.rocketmq.shaded.com.google.gson.Gson;
|
||||
|
||||
public class RocketmqProducer {
|
||||
private RocketmqProducer() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws ClientException, IOException {
|
||||
if (args.length < 2) {
|
||||
System.out.println("Usage: rocketmq_producer <endpoints> <topic>");
|
||||
return;
|
||||
}
|
||||
final ClientServiceProvider provider = ClientServiceProvider.loadService();
|
||||
String endpoints = args[0];
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.newBuilder()
|
||||
.setEndpoints(endpoints)
|
||||
.build();
|
||||
String topic = args[1];
|
||||
|
||||
// Create the RocketMQ Producer.
|
||||
final Producer producer = provider.newProducerBuilder()
|
||||
.setClientConfiguration(clientConfiguration)
|
||||
.setTopics(topic)
|
||||
.build();
|
||||
final Gson gson = new Gson();
|
||||
Map<String, String> payload = new HashMap<>();
|
||||
payload.put("foo", "bar");
|
||||
final CloudEvent event = new CloudEventBuilder()
|
||||
.withId("client-id")
|
||||
.withSource(URI.create("http://127.0.0.1/rocketmq-client"))
|
||||
.withType("com.foobar")
|
||||
.withTime(Time.parseTime("2022-11-09T21:47:12.032198+00:00"))
|
||||
.withData(gson.toJson(payload).getBytes(StandardCharsets.UTF_8))
|
||||
.build();
|
||||
// Transform event into message.
|
||||
final Message message = RocketMqMessageFactory.createWriter(topic).writeBinary(event);
|
||||
try {
|
||||
// Send the message.
|
||||
final SendReceipt sendReceipt = producer.send(message);
|
||||
System.out.println("Send message successfully, messageId=" + sendReceipt.getMessageId());
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to send message");
|
||||
e.printStackTrace();
|
||||
}
|
||||
// Close the producer when you don't need it anymore.
|
||||
producer.close();
|
||||
}
|
||||
}
|
|
@ -5,13 +5,14 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloudevents-spring-function-example</artifactId>
|
||||
|
||||
<properties>
|
||||
<module-name>cloudevents.example.spring.function</module-name>
|
||||
<spring-boot.version>2.4.3</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloudevents-spring-reactive-example</artifactId>
|
||||
|
||||
<properties>
|
||||
<module-name>cloudevents.example.spring.reactive</module-name>
|
||||
<spring-boot.version>2.4.3</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.cloudevents.examples.spring;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -76,7 +77,7 @@ public class DemoApplicationTests {
|
|||
.bodyValue(CloudEventBuilder.v1() //
|
||||
.withId("12345") //
|
||||
.withType("io.spring.event") //
|
||||
.withSource(URI.create("https://spring.io/events")).withData("{\"value\":\"Dave\"}".getBytes()) //
|
||||
.withSource(URI.create("https://spring.io/events")).withData("{\"value\":\"Dave\"}".getBytes(StandardCharsets.UTF_8)) //
|
||||
.build()) //
|
||||
.exchange() //
|
||||
.expectStatus().isOk() //
|
||||
|
@ -102,7 +103,7 @@ public class DemoApplicationTests {
|
|||
.withId("12345") //
|
||||
.withType("io.spring.event") //
|
||||
.withSource(URI.create("https://spring.io/events")) //
|
||||
.withData("{\"value\":\"Dave\"}".getBytes()) //
|
||||
.withData("{\"value\":\"Dave\"}".getBytes(StandardCharsets.UTF_8)) //
|
||||
.build()) //
|
||||
.exchange() //
|
||||
.expectStatus().isOk() //
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.cloudevents.examples.spring;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -38,7 +39,7 @@ public class WebClientTests {
|
|||
.withId("12345") //
|
||||
.withSource(URI.create("https://spring.io/events")) //
|
||||
.withType("io.spring.event") //
|
||||
.withData("{\"value\":\"Dave\"}".getBytes()) //
|
||||
.withData("{\"value\":\"Dave\"}".getBytes(StandardCharsets.UTF_8)) //
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloudevents-spring-rsocket-example</artifactId>
|
||||
|
||||
<properties>
|
||||
<module-name>cloudevents.example.spring.rsocket</module-name>
|
||||
<spring-boot.version>2.4.3</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -5,11 +5,14 @@
|
|||
<parent>
|
||||
<artifactId>cloudevents-examples</artifactId>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cloudevents-vertx-example</artifactId>
|
||||
<properties>
|
||||
<module-name>cloudevents.example.vertx</module-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
@ -12,6 +12,7 @@ import io.vertx.ext.web.client.HttpResponse;
|
|||
import io.vertx.ext.web.client.WebClient;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SampleHTTPClient {
|
||||
|
@ -31,7 +32,7 @@ public class SampleHTTPClient {
|
|||
|
||||
// Create an event template to set basic CloudEvent attributes.
|
||||
CloudEventBuilder eventTemplate = CloudEventBuilder.v1()
|
||||
.withSource(URI.create("https://github.com/cloudevents/sdk-java/tree/master/examples/vertx"))
|
||||
.withSource(URI.create("https://github.com/cloudevents/sdk-java/tree/main/examples/vertx"))
|
||||
.withType("vertx.example");
|
||||
|
||||
// Send NUM_EVENTS events.
|
||||
|
@ -41,7 +42,7 @@ public class SampleHTTPClient {
|
|||
// Create the event starting from the template
|
||||
final CloudEvent event = eventTemplate.newBuilder()
|
||||
.withId(UUID.randomUUID().toString())
|
||||
.withData("text/plain", data.getBytes())
|
||||
.withData("text/plain", data.getBytes(StandardCharsets.UTF_8))
|
||||
.build();
|
||||
|
||||
Future<HttpResponse<Buffer>> responseFuture;
|
||||
|
|
|
@ -23,7 +23,7 @@ public class SampleHTTPServer {
|
|||
|
||||
// We need to read the event from the HTTP request we get, so create a MessageReader.
|
||||
VertxMessageFactory.createReader(request)
|
||||
// Covert the MessageReader to a CloudEvent.
|
||||
// Convert the MessageReader to a CloudEvent.
|
||||
.map(MessageReader::toEvent)
|
||||
.onSuccess(event -> {
|
||||
// Print out the event.
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2021-Present The CloudEvents Authors
|
||||
~ <p>
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~ <p>
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~ <p>
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
~
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>cloudevents-avro-compact</artifactId>
|
||||
<name>CloudEvents - Avro Compact</name>
|
||||
|
||||
|
||||
<properties>
|
||||
<module-name>io.cloudevents.formats.avro.compact</module-name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.avro</groupId>
|
||||
<artifactId>avro-maven-plugin</artifactId>
|
||||
<version>1.11.2</version>
|
||||
<configuration>
|
||||
<stringType>String</stringType>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>schema</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.avro</groupId>
|
||||
<artifactId>avro</artifactId>
|
||||
<version>1.11.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test deps -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>2.0.16</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj-core.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"namespace": "io.cloudevents.v1.avro.compact",
|
||||
"type": "record",
|
||||
"name": "CloudEvent",
|
||||
"version": "1.0",
|
||||
"doc": "Avro Compact Event Format for CloudEvents",
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "datacontenttype",
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
{
|
||||
"name": "dataschema",
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
{
|
||||
"name": "subject",
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
{
|
||||
"name": "time",
|
||||
"type": [
|
||||
"null",
|
||||
{
|
||||
"type": "long",
|
||||
"logicalType": "timestamp-micros"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
{
|
||||
"name": "extensions",
|
||||
"type": {
|
||||
"type": "map",
|
||||
"values": [
|
||||
"boolean",
|
||||
"int",
|
||||
{
|
||||
"type": "long",
|
||||
"logicalType" : "timestamp-micros"
|
||||
},
|
||||
"string",
|
||||
"bytes"
|
||||
]
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"type": [
|
||||
"bytes",
|
||||
"null"
|
||||
],
|
||||
"default": "null"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright 2018-Present The CloudEvents Authors
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package io.cloudevents.avro.compact;
|
||||
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.CloudEventData;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
import io.cloudevents.core.data.BytesCloudEventData;
|
||||
import io.cloudevents.core.format.EventDeserializationException;
|
||||
import io.cloudevents.core.format.EventFormat;
|
||||
import io.cloudevents.core.format.EventSerializationException;
|
||||
import io.cloudevents.rw.CloudEventDataMapper;
|
||||
import io.cloudevents.v1.avro.compact.CloudEvent.Builder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of {@link EventFormat} for the Avro Compact format.
|
||||
* This format is resolvable with {@link io.cloudevents.core.provider.EventFormatProvider} using the content type {@link #AVRO_COMPACT_CONTENT_TYPE}.
|
||||
*/
|
||||
public class AvroCompactFormat implements EventFormat {
|
||||
|
||||
public static final String AVRO_COMPACT_CONTENT_TYPE = "application/cloudevents+avrocompact";
|
||||
|
||||
@Override
|
||||
public byte[] serialize(CloudEvent from) throws EventSerializationException {
|
||||
try {
|
||||
Builder to = io.cloudevents.v1.avro.compact.CloudEvent.newBuilder();
|
||||
|
||||
// extensions
|
||||
Map<String, Object> extensions = new HashMap<>();
|
||||
for (String name : from.getExtensionNames()) {
|
||||
Object value = from.getExtension(name);
|
||||
if (value instanceof byte[])
|
||||
value = ByteBuffer.wrap((byte[]) value);
|
||||
else if (value instanceof OffsetDateTime)
|
||||
value = ((OffsetDateTime) value).toInstant();
|
||||
extensions.put(name, value);
|
||||
}
|
||||
|
||||
to.setSource(from.getSource().toString())
|
||||
.setType(from.getType())
|
||||
.setId(from.getId())
|
||||
.setSubject(from.getSubject())
|
||||
.setDatacontenttype(from.getDataContentType())
|
||||
.setExtensions(extensions);
|
||||
|
||||
if (from.getTime() != null)
|
||||
to.setTime(from.getTime().toInstant());
|
||||
if (from.getDataSchema() != null)
|
||||
to.setDataschema(from.getDataSchema().toString());
|
||||
|
||||
CloudEventData data = from.getData();
|
||||
if (data != null)
|
||||
to.setData(ByteBuffer.wrap(data.toBytes()));
|
||||
return to.build().toByteBuffer().array();
|
||||
} catch (Exception e) {
|
||||
throw new EventSerializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent deserialize(byte[] bytes, CloudEventDataMapper<? extends CloudEventData> mapper) throws EventDeserializationException {
|
||||
try {
|
||||
io.cloudevents.v1.avro.compact.CloudEvent from = io.cloudevents.v1.avro.compact.CloudEvent.fromByteBuffer(ByteBuffer.wrap(bytes));
|
||||
CloudEventBuilder to = CloudEventBuilder.v1()
|
||||
.withSource(URI.create(from.getSource()))
|
||||
.withType(from.getType())
|
||||
.withId(from.getType())
|
||||
.withSubject(from.getSubject())
|
||||
.withDataContentType(from.getDatacontenttype());
|
||||
|
||||
if (from.getTime() != null)
|
||||
to.withTime(from.getTime().atOffset(ZoneOffset.UTC));
|
||||
if (from.getDataschema() != null)
|
||||
to.withDataSchema(URI.create(from.getDataschema()));
|
||||
|
||||
// extensions
|
||||
for (Map.Entry<String, Object> entry : from.getExtensions().entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
// Avro supports boolean, int, string, bytes
|
||||
if (value instanceof Boolean)
|
||||
to.withExtension(name, (boolean) value);
|
||||
else if (value instanceof Integer)
|
||||
to.withExtension(name, (int) value);
|
||||
else if (value instanceof Instant)
|
||||
to.withExtension(name, ((Instant) value).atOffset(ZoneOffset.UTC));
|
||||
else if (value instanceof String)
|
||||
to.withExtension(name, (String) value);
|
||||
else if (value instanceof ByteBuffer)
|
||||
to.withExtension(name, ((ByteBuffer) value).array());
|
||||
else
|
||||
// this cannot happen, if ever seen, must be bug in this library
|
||||
throw new AssertionError(String.format("invalid extension %s unsupported type %s", name, value.getClass()));
|
||||
}
|
||||
|
||||
if (from.getData() == null)
|
||||
return to.end();
|
||||
else {
|
||||
CloudEventData data = BytesCloudEventData.wrap(from.getData().array());
|
||||
return to.end(mapper.map(data));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new EventDeserializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serializedContentType() {
|
||||
return AVRO_COMPACT_CONTENT_TYPE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
io.cloudevents.avro.compact.AvroCompactFormat
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2018-Present The CloudEvents Authors
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package io.cloudevents.avro.compact;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
import io.cloudevents.core.data.BytesCloudEventData;
|
||||
import io.cloudevents.core.format.EventFormat;
|
||||
import io.cloudevents.core.provider.EventFormatProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class AvroCompactFormatTest {
|
||||
|
||||
private final EventFormat format = EventFormatProvider.getInstance().resolveFormat(AvroCompactFormat.AVRO_COMPACT_CONTENT_TYPE);
|
||||
|
||||
@Test
|
||||
void format() {
|
||||
assertNotNull(format);
|
||||
assertEquals(Collections.singleton("application/cloudevents+avrocompact"), format.deserializableContentTypes());
|
||||
|
||||
CloudEvent event = CloudEventBuilder.v1()
|
||||
// mandatory
|
||||
.withId("")
|
||||
.withSource(URI.create(""))
|
||||
.withType("")
|
||||
// optional
|
||||
.withTime(Instant.EPOCH.atOffset(ZoneOffset.UTC))
|
||||
.withSubject("subject")
|
||||
.withDataSchema(URI.create(""))
|
||||
// extension
|
||||
// support boolean, int, long, string, bytes
|
||||
.withExtension("boolean", false)
|
||||
.withExtension("int", 0)
|
||||
.withExtension("time", Instant.EPOCH.atOffset(ZoneOffset.UTC))
|
||||
.withExtension("string", "")
|
||||
// omitting bytes, because it is not supported by CloudEvent.equals
|
||||
.withData("", BytesCloudEventData.wrap(new byte[0]))
|
||||
.build();
|
||||
|
||||
byte[] serialized = format.serialize(event);
|
||||
|
||||
assertNotNull(serialized);
|
||||
|
||||
CloudEvent deserialized = format.deserialize(serialized);
|
||||
|
||||
assertEquals(event, deserialized);
|
||||
|
||||
byte[] reserialized = format.serialize(deserialized);
|
||||
|
||||
assertArrayEquals(serialized, reserialized);
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
|||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<jackson.version>2.13.3</jackson.version>
|
||||
<jackson.version>2.15.2</jackson.version>
|
||||
<module-name>io.cloudevents.formats.jackson</module-name>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ import io.cloudevents.core.builder.CloudEventBuilder;
|
|||
import io.cloudevents.core.data.BytesCloudEventData;
|
||||
import io.cloudevents.rw.*;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
@ -41,18 +42,21 @@ import java.io.IOException;
|
|||
class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
||||
private final boolean forceExtensionNameLowerCaseDeserialization;
|
||||
private final boolean forceIgnoreInvalidExtensionNameDeserialization;
|
||||
private final boolean disableDataContentTypeDefaulting;
|
||||
|
||||
protected CloudEventDeserializer() {
|
||||
this(false, false);
|
||||
this(false, false, false);
|
||||
}
|
||||
|
||||
protected CloudEventDeserializer(
|
||||
boolean forceExtensionNameLowerCaseDeserialization,
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization,
|
||||
boolean disableDataContentTypeDefaulting
|
||||
) {
|
||||
super(CloudEvent.class);
|
||||
this.forceExtensionNameLowerCaseDeserialization = forceExtensionNameLowerCaseDeserialization;
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization = forceIgnoreInvalidExtensionNameDeserialization;
|
||||
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
|
||||
}
|
||||
|
||||
private static class JsonMessage implements CloudEventReader {
|
||||
|
@ -61,17 +65,20 @@ class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
|||
private final ObjectNode node;
|
||||
private final boolean forceExtensionNameLowerCaseDeserialization;
|
||||
private final boolean forceIgnoreInvalidExtensionNameDeserialization;
|
||||
private final boolean disableDataContentTypeDefaulting;
|
||||
|
||||
public JsonMessage(
|
||||
JsonParser p,
|
||||
ObjectNode node,
|
||||
boolean forceExtensionNameLowerCaseDeserialization,
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization,
|
||||
boolean disableDataContentTypeDefaulting
|
||||
) {
|
||||
this.p = p;
|
||||
this.node = node;
|
||||
this.forceExtensionNameLowerCaseDeserialization = forceExtensionNameLowerCaseDeserialization;
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization = forceIgnoreInvalidExtensionNameDeserialization;
|
||||
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,6 +98,9 @@ class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
|||
|
||||
// Parse datacontenttype if any
|
||||
String contentType = getOptionalStringNode(this.node, this.p, "datacontenttype");
|
||||
if (!this.disableDataContentTypeDefaulting && contentType == null && this.node.has("data")) {
|
||||
contentType = "application/json";
|
||||
}
|
||||
if (contentType != null) {
|
||||
writer.withContextAttribute("datacontenttype", contentType);
|
||||
}
|
||||
|
@ -118,11 +128,11 @@ class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
|||
if (JsonFormat.dataIsJsonContentType(contentType)) {
|
||||
// This solution is quite bad, but i see no alternatives now.
|
||||
// Hopefully in future we can improve it
|
||||
data = new JsonCloudEventData(node.remove("data"));
|
||||
data = JsonCloudEventData.wrap(node.remove("data"));
|
||||
} else {
|
||||
JsonNode dataNode = node.remove("data");
|
||||
assertNodeType(dataNode, JsonNodeType.STRING, "data", "Because content type is not a json, only a string is accepted as data");
|
||||
data = BytesCloudEventData.wrap(dataNode.asText().getBytes());
|
||||
data = BytesCloudEventData.wrap(dataNode.asText().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,11 +146,11 @@ class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
|||
if (JsonFormat.dataIsJsonContentType(contentType)) {
|
||||
// This solution is quite bad, but i see no alternatives now.
|
||||
// Hopefully in future we can improve it
|
||||
data = new JsonCloudEventData(node.remove("data"));
|
||||
data = JsonCloudEventData.wrap(node.remove("data"));
|
||||
} else {
|
||||
JsonNode dataNode = node.remove("data");
|
||||
assertNodeType(dataNode, JsonNodeType.STRING, "data", "Because content type is not a json, only a string is accepted as data");
|
||||
data = BytesCloudEventData.wrap(dataNode.asText().getBytes());
|
||||
data = BytesCloudEventData.wrap(dataNode.asText().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +242,7 @@ class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
|||
*
|
||||
* @param name the extension name
|
||||
* @return true if extension name is valid, false otherwise
|
||||
* @see <a href="https://github.com/cloudevents/spec/blob/master/spec.md#attribute-naming-convention">attribute-naming-convention</a>
|
||||
* @see <a href="https://github.com/cloudevents/spec/blob/main/spec.md#attribute-naming-convention">attribute-naming-convention</a>
|
||||
*/
|
||||
private boolean isValidExtensionName(String name) {
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
|
@ -256,7 +266,7 @@ class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
|||
ObjectNode node = ctxt.readValue(p, ObjectNode.class);
|
||||
|
||||
try {
|
||||
return new JsonMessage(p, node, this.forceExtensionNameLowerCaseDeserialization, this.forceIgnoreInvalidExtensionNameDeserialization)
|
||||
return new JsonMessage(p, node, this.forceExtensionNameLowerCaseDeserialization, this.forceIgnoreInvalidExtensionNameDeserialization, this.disableDataContentTypeDefaulting)
|
||||
.read(CloudEventBuilder::fromSpecVersion);
|
||||
} catch (RuntimeException e) {
|
||||
// Yeah this is bad but it's needed to support checked exceptions...
|
||||
|
|
|
@ -20,6 +20,7 @@ package io.cloudevents.jackson;
|
|||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import io.cloudevents.CloudEventData;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
|
@ -40,7 +41,7 @@ public class JsonCloudEventData implements CloudEventData {
|
|||
|
||||
@Override
|
||||
public byte[] toBytes() {
|
||||
return node.toString().getBytes();
|
||||
return node.toString().getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule;
|
|||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.CloudEventData;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
import io.cloudevents.core.format.ContentType;
|
||||
import io.cloudevents.core.format.EventDeserializationException;
|
||||
import io.cloudevents.core.format.EventFormat;
|
||||
import io.cloudevents.core.format.EventSerializationException;
|
||||
|
@ -47,7 +48,7 @@ public final class JsonFormat implements EventFormat {
|
|||
/**
|
||||
* JSON Data Content Type Discriminator
|
||||
*/
|
||||
private static final Pattern JSON_CONTENT_TYPE_PATTERN = Pattern.compile("^(application|text)\\/([a-zA-Z]+\\+)?json$");
|
||||
private static final Pattern JSON_CONTENT_TYPE_PATTERN = Pattern.compile("^(application|text)\\/([a-zA-Z]+\\+)?json(;.*)*$");
|
||||
private final ObjectMapper mapper;
|
||||
private final JsonFormatOptions options;
|
||||
|
||||
|
@ -212,7 +213,7 @@ public final class JsonFormat implements EventFormat {
|
|||
ceModule.addSerializer(CloudEvent.class, new CloudEventSerializer(
|
||||
options.isForceDataBase64Serialization(), options.isForceStringSerialization()));
|
||||
ceModule.addDeserializer(CloudEvent.class, new CloudEventDeserializer(
|
||||
options.isForceExtensionNameLowerCaseDeserialization(), options.isForceIgnoreInvalidExtensionNameDeserialization()));
|
||||
options.isForceExtensionNameLowerCaseDeserialization(), options.isForceIgnoreInvalidExtensionNameDeserialization(), options.isDataContentTypeDefaultingDisabled()));
|
||||
return ceModule;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,24 +21,27 @@ public final class JsonFormatOptions {
|
|||
private final boolean forceStringSerialization;
|
||||
private final boolean forceExtensionNameLowerCaseDeserialization;
|
||||
private final boolean forceIgnoreInvalidExtensionNameDeserialization;
|
||||
private final boolean disableDataContentTypeDefaulting;
|
||||
|
||||
/**
|
||||
* Create a new instance of this class options the serialization / deserialization.
|
||||
*/
|
||||
public JsonFormatOptions() {
|
||||
this(false, false, false, false);
|
||||
this(false, false, false, false, false);
|
||||
}
|
||||
|
||||
JsonFormatOptions(
|
||||
boolean forceDataBase64Serialization,
|
||||
boolean forceStringSerialization,
|
||||
boolean forceExtensionNameLowerCaseDeserialization,
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization,
|
||||
boolean disableDataContentTypeDefaulting
|
||||
) {
|
||||
this.forceDataBase64Serialization = forceDataBase64Serialization;
|
||||
this.forceStringSerialization = forceStringSerialization;
|
||||
this.forceExtensionNameLowerCaseDeserialization = forceExtensionNameLowerCaseDeserialization;
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization = forceIgnoreInvalidExtensionNameDeserialization;
|
||||
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
|
||||
}
|
||||
|
||||
public static JsonFormatOptionsBuilder builder() {
|
||||
|
@ -61,11 +64,14 @@ public final class JsonFormatOptions {
|
|||
return this.forceIgnoreInvalidExtensionNameDeserialization;
|
||||
}
|
||||
|
||||
public boolean isDataContentTypeDefaultingDisabled() { return this.disableDataContentTypeDefaulting; }
|
||||
|
||||
public static class JsonFormatOptionsBuilder {
|
||||
private boolean forceDataBase64Serialization = false;
|
||||
private boolean forceStringSerialization = false;
|
||||
private boolean forceExtensionNameLowerCaseDeserialization = false;
|
||||
private boolean forceIgnoreInvalidExtensionNameDeserialization = false;
|
||||
private boolean disableDataContentTypeDefaulting = false;
|
||||
|
||||
public JsonFormatOptionsBuilder forceDataBase64Serialization(boolean forceDataBase64Serialization) {
|
||||
this.forceDataBase64Serialization = forceDataBase64Serialization;
|
||||
|
@ -87,12 +93,18 @@ public final class JsonFormatOptions {
|
|||
return this;
|
||||
}
|
||||
|
||||
public JsonFormatOptionsBuilder disableDataContentTypeDefaulting(boolean disableDataContentTypeDefaulting) {
|
||||
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JsonFormatOptions build() {
|
||||
return new JsonFormatOptions(
|
||||
this.forceDataBase64Serialization,
|
||||
this.forceStringSerialization,
|
||||
this.forceExtensionNameLowerCaseDeserialization,
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization,
|
||||
this.disableDataContentTypeDefaulting
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package io.cloudevents.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
||||
import static io.cloudevents.jackson.JsonFormat.getCloudEventJacksonModule;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class CloudEventDeserializerTest {
|
||||
|
||||
private static final String nonBinaryPayload = "{\n" +
|
||||
" \"specversion\" : \"1.0\",\n" +
|
||||
" \"type\" : \"com.example.someevent\",\n" +
|
||||
" \"source\" : \"/mycontext\",\n" +
|
||||
" \"subject\": null,\n" +
|
||||
" \"id\" : \"D234-1234-1234\",\n" +
|
||||
" \"time\" : \"2018-04-05T17:31:00Z\",\n" +
|
||||
" \"comexampleextension1\" : \"value\",\n" +
|
||||
" \"comexampleothervalue\" : 5,\n" +
|
||||
" \"data\" : \"I'm just a string\"\n" +
|
||||
"}";
|
||||
|
||||
private static final String binaryPayload = "{\n" +
|
||||
" \"specversion\" : \"1.0\",\n" +
|
||||
" \"type\" : \"com.example.someevent\",\n" +
|
||||
" \"source\" : \"/mycontext\",\n" +
|
||||
" \"id\" : \"D234-1234-1234\",\n" +
|
||||
" \"data_base64\" : \"eyAieHl6IjogMTIzIH0=\"\n" +
|
||||
"}";
|
||||
|
||||
@Test
|
||||
void impliedDataContentTypeNonBinaryData() throws IOException {
|
||||
ObjectMapper mapper = getObjectMapper(false);
|
||||
StringReader reader = new StringReader(nonBinaryPayload);
|
||||
CloudEvent ce = mapper.readValue(reader, CloudEvent.class);
|
||||
assertThat(ce.getDataContentType()).isEqualTo("application/json");
|
||||
|
||||
mapper = getObjectMapper(true);
|
||||
reader = new StringReader(nonBinaryPayload);
|
||||
ce = mapper.readValue(reader, CloudEvent.class);
|
||||
assertThat(ce.getDataContentType()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void impliedDataContentTypeBinaryData() throws IOException {
|
||||
final ObjectMapper mapper = getObjectMapper(false);
|
||||
StringReader reader = new StringReader(binaryPayload);
|
||||
CloudEvent ce = mapper.readValue(reader, CloudEvent.class);
|
||||
assertThat(ce.getDataContentType()).isNull();
|
||||
}
|
||||
|
||||
private static ObjectMapper getObjectMapper(boolean disableDataContentTypeDefaulting) {
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final SimpleModule module = getCloudEventJacksonModule(
|
||||
JsonFormatOptions
|
||||
.builder()
|
||||
.disableDataContentTypeDefaulting(disableDataContentTypeDefaulting)
|
||||
.build()
|
||||
);
|
||||
mapper.registerModule(module);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
}
|
|
@ -37,7 +37,7 @@ public class JsonCloudEventDataTest {
|
|||
@MethodSource("textContentArguments")
|
||||
public void testMapper(String contentType) {
|
||||
CloudEvent event = CloudEventBuilder.v1(Data.V1_MIN)
|
||||
.withData(contentType, new JsonCloudEventData(JsonNodeFactory.instance.numberNode(10)))
|
||||
.withData(contentType, JsonCloudEventData.wrap(JsonNodeFactory.instance.numberNode(10)))
|
||||
.build();
|
||||
|
||||
byte[] serialized = EventFormatProvider.getInstance().resolveFormat(JsonFormat.CONTENT_TYPE)
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
|||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.SpecVersion;
|
||||
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||
import io.cloudevents.core.format.ContentType;
|
||||
import io.cloudevents.core.format.EventDeserializationException;
|
||||
import io.cloudevents.core.provider.EventFormatProvider;
|
||||
import io.cloudevents.rw.CloudEventRWException;
|
||||
|
@ -40,6 +41,7 @@ import java.nio.file.Paths;
|
|||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static io.cloudevents.core.format.ContentType.*;
|
||||
import static io.cloudevents.core.test.Data.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
|
@ -47,6 +49,22 @@ class JsonFormatTest {
|
|||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("jsonContentTypes")
|
||||
void isJsonContentType(String contentType) {
|
||||
boolean json = JsonFormat.dataIsJsonContentType(contentType);
|
||||
|
||||
assertThat(json).isTrue();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("wrongJsonContentTypes")
|
||||
void isNotJsonContentType(String contentType) {
|
||||
boolean json = JsonFormat.dataIsJsonContentType(contentType);
|
||||
|
||||
assertThat(json).isFalse();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("serializeTestArgumentsDefault")
|
||||
void serialize(CloudEvent input, String outputFile) throws IOException {
|
||||
|
@ -151,6 +169,42 @@ class JsonFormatTest {
|
|||
|
||||
}
|
||||
|
||||
static Stream<Arguments> jsonContentTypes() {
|
||||
return Stream.of(
|
||||
Arguments.of("application/json"),
|
||||
Arguments.of("application/json;charset=utf-8"),
|
||||
Arguments.of("application/json;\tcharset = \"utf-8\""),
|
||||
Arguments.of("application/cloudevents+json;charset=UTF-8"),
|
||||
Arguments.of("text/json"),
|
||||
Arguments.of("text/json;charset=utf-8"),
|
||||
Arguments.of("text/cloudevents+json;charset=UTF-8"),
|
||||
Arguments.of("text/json;\twhatever"),
|
||||
Arguments.of("text/json; boundary=something"),
|
||||
Arguments.of("text/json;foo=\"bar\""),
|
||||
Arguments.of("text/json; charset = \"us-ascii\""),
|
||||
Arguments.of("text/json; \t"),
|
||||
Arguments.of("text/json;"),
|
||||
//https://www.rfc-editor.org/rfc/rfc2045#section-5.1
|
||||
// any us-ascii char can be part of parameters (except CTRLs and tspecials)
|
||||
Arguments.of("text/json; char-set = $!#$%&'*+.^_`|"),
|
||||
Arguments.of((Object) null),
|
||||
Arguments.of(JSON + ""),
|
||||
Arguments.of(JSON.value()),
|
||||
Arguments.of(JSON.toString())
|
||||
);
|
||||
}
|
||||
|
||||
static Stream<Arguments> wrongJsonContentTypes() {
|
||||
return Stream.of(
|
||||
Arguments.of("applications/json"),
|
||||
Arguments.of("application/jsom"),
|
||||
Arguments.of("application/jsonwrong"),
|
||||
Arguments.of("text/json "),
|
||||
Arguments.of("text/json ;"),
|
||||
Arguments.of("test/json")
|
||||
);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> serializeTestArgumentsDefault() {
|
||||
return Stream.of(
|
||||
Arguments.of(V03_MIN, "v03/min.json"),
|
||||
|
@ -258,7 +312,7 @@ class JsonFormatTest {
|
|||
}
|
||||
|
||||
private JsonFormat getFormat() {
|
||||
return (JsonFormat) EventFormatProvider.getInstance().resolveFormat(JsonFormat.CONTENT_TYPE);
|
||||
return (JsonFormat) EventFormatProvider.getInstance().resolveFormat(JSON);
|
||||
}
|
||||
|
||||
private static byte[] loadFile(String input) {
|
||||
|
@ -266,7 +320,7 @@ class JsonFormatTest {
|
|||
return String.join(
|
||||
"",
|
||||
Files.readAllLines(Paths.get(Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource(input)).toURI()), StandardCharsets.UTF_8)
|
||||
).getBytes();
|
||||
).getBytes(StandardCharsets.UTF_8);
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -281,7 +335,7 @@ class JsonFormatTest {
|
|||
builder = CloudEventBuilder.v03(event);
|
||||
}
|
||||
return builder
|
||||
.withData(new JsonCloudEventData(JsonNodeFactory.instance.objectNode()))
|
||||
.withData(JsonCloudEventData.wrap(JsonNodeFactory.instance.objectNode()))
|
||||
.build();
|
||||
} else {
|
||||
return event;
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -28,7 +29,7 @@ public class PojoCloudEventDataMapperTest {
|
|||
public void testWithBytes(PojoCloudEventDataMapper<MyPojo> mapper) {
|
||||
|
||||
CloudEvent event = CloudEventBuilder.v1(Data.V1_MIN)
|
||||
.withData("application/json", myPojoSerialized.getBytes())
|
||||
.withData("application/json", myPojoSerialized.getBytes(StandardCharsets.UTF_8))
|
||||
.build();
|
||||
|
||||
PojoCloudEventData<MyPojo> mappedData = CloudEventUtils.mapData(
|
||||
|
@ -46,7 +47,7 @@ public class PojoCloudEventDataMapperTest {
|
|||
public void testWithJson(PojoCloudEventDataMapper<MyPojo> mapper) {
|
||||
|
||||
CloudEvent event = CloudEventBuilder.v1(Data.V1_MIN)
|
||||
.withData("application/json", new JsonCloudEventData(myPojoJson))
|
||||
.withData("application/json", JsonCloudEventData.wrap(myPojoJson))
|
||||
.build();
|
||||
|
||||
PojoCloudEventData<MyPojo> mappedData = CloudEventUtils.mapData(
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
|||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<protobuf.version>3.15.0</protobuf.version>
|
||||
<protobuf.version>4.31.1</protobuf.version>
|
||||
<module-name>io.cloudevents.formats.protobuf</module-name>
|
||||
</properties>
|
||||
|
||||
|
@ -112,7 +112,7 @@
|
|||
<dependency>
|
||||
<groupId>com.google.truth.extensions</groupId>
|
||||
<artifactId>truth-proto-extension</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>1.4.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package io.cloudevents.protobuf;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.Message;
|
||||
import io.cloudevents.CloudEventData;
|
||||
|
||||
|
@ -26,13 +27,7 @@ import io.cloudevents.CloudEventData;
|
|||
public interface ProtoCloudEventData extends CloudEventData {
|
||||
|
||||
/**
|
||||
* Gets the protobuf {@link Message} representation of this data.
|
||||
* @return The data as a {@link Message}
|
||||
*/
|
||||
Message getMessage();
|
||||
|
||||
/**
|
||||
* Convenience helper to wrap a Protobuf Message as
|
||||
* Convenience helper to wrap a Protobuf {@link Message} as
|
||||
* CloudEventData.
|
||||
*
|
||||
* @param protoMessage The message to wrap
|
||||
|
@ -41,4 +36,11 @@ public interface ProtoCloudEventData extends CloudEventData {
|
|||
static CloudEventData wrap(Message protoMessage) {
|
||||
return new ProtoDataWrapper(protoMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the protobuf {@link Any} representation of this data.
|
||||
*
|
||||
* @return The data as an {@link Any}
|
||||
*/
|
||||
Any getAny();
|
||||
}
|
||||
|
|
|
@ -16,23 +16,74 @@
|
|||
*/
|
||||
package io.cloudevents.protobuf;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
class ProtoDataWrapper implements ProtoCloudEventData {
|
||||
|
||||
private final Message protoMessage;
|
||||
private final Any protoAny;
|
||||
|
||||
ProtoDataWrapper(Message protoMessage) {
|
||||
this.protoMessage = protoMessage;
|
||||
|
||||
Objects.requireNonNull(protoMessage);
|
||||
|
||||
if (protoMessage instanceof Any) {
|
||||
protoAny = (Any) protoMessage;
|
||||
} else {
|
||||
protoAny = Any.pack(protoMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getMessage() {
|
||||
return protoMessage;
|
||||
public Any getAny() {
|
||||
return protoAny;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toBytes() {
|
||||
return protoMessage.toByteArray();
|
||||
return protoAny.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return (true);
|
||||
}
|
||||
|
||||
if (!(obj instanceof ProtoDataWrapper)) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
// Now compare the actual data
|
||||
ProtoDataWrapper rhs = (ProtoDataWrapper) obj;
|
||||
|
||||
final Any lhsAny = getAny();
|
||||
final Any rhsAny = rhs.getAny();
|
||||
|
||||
// This is split out for readability.
|
||||
// 1. Sanity compare the 'Any' references.
|
||||
// 2. Compare the content in terms onf an 'Any'.
|
||||
// - Verify the types match
|
||||
// - Verify the values match.
|
||||
|
||||
// NULL checks not required as object cannot be built
|
||||
// with a null.
|
||||
|
||||
if (lhsAny == rhsAny) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final boolean typesMatch = (ProtoSupport.extractMessageType(lhsAny).equals(ProtoSupport.extractMessageType(rhsAny)));
|
||||
|
||||
if (typesMatch) {
|
||||
return lhsAny.getValue().equals(rhsAny.getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,18 +16,14 @@
|
|||
*/
|
||||
package io.cloudevents.protobuf;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import io.cloudevents.CloudEventData;
|
||||
import io.cloudevents.SpecVersion;
|
||||
import io.cloudevents.core.data.BytesCloudEventData;
|
||||
import io.cloudevents.core.v1.CloudEventV1;
|
||||
import io.cloudevents.rw.CloudEventDataMapper;
|
||||
import io.cloudevents.rw.CloudEventRWException;
|
||||
import io.cloudevents.rw.CloudEventReader;
|
||||
import io.cloudevents.rw.CloudEventWriter;
|
||||
import io.cloudevents.rw.CloudEventWriterFactory;
|
||||
import io.cloudevents.rw.*;
|
||||
import io.cloudevents.v1.proto.CloudEvent;
|
||||
import io.cloudevents.v1.proto.CloudEvent.CloudEventAttributeValue;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
|
@ -105,7 +101,7 @@ class ProtoDeserializer implements CloudEventReader {
|
|||
data = BytesCloudEventData.wrap(raw);
|
||||
break;
|
||||
case PROTO_DATA:
|
||||
data = new ProtoAccessor(this.protoCe);
|
||||
data = new ProtoDataWrapper(this.protoCe.getProtoData());
|
||||
break;
|
||||
case DATA_NOT_SET:
|
||||
break;
|
||||
|
@ -130,22 +126,4 @@ class ProtoDeserializer implements CloudEventReader {
|
|||
return instant.atOffset(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
private static class ProtoAccessor implements ProtoCloudEventData {
|
||||
|
||||
private final Message message;
|
||||
|
||||
ProtoAccessor(CloudEvent proto){
|
||||
this.message = proto.getProtoData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toBytes() {
|
||||
return message.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import io.cloudevents.rw.CloudEventContextWriter;
|
|||
import io.cloudevents.rw.CloudEventRWException;
|
||||
import io.cloudevents.rw.CloudEventWriter;
|
||||
import io.cloudevents.v1.proto.CloudEvent;
|
||||
import io.cloudevents.v1.proto.CloudEvent.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Instant;
|
||||
|
@ -247,10 +246,20 @@ class ProtoSerializer {
|
|||
|
||||
// If it's a proto message we can handle that directly.
|
||||
if (data instanceof ProtoCloudEventData) {
|
||||
|
||||
final ProtoCloudEventData protoData = (ProtoCloudEventData) data;
|
||||
if (protoData.getMessage() != null) {
|
||||
protoBuilder.setProtoData(Any.pack(protoData.getMessage()));
|
||||
final Any anAny = protoData.getAny();
|
||||
|
||||
// Even though our local implementation cannot be instantiated
|
||||
// with NULL data nothing stops somebody from having their own
|
||||
// variant that isn't as 'safe'.
|
||||
|
||||
if (anAny != null) {
|
||||
protoBuilder.setProtoData(anAny);
|
||||
} else {
|
||||
throw CloudEventRWException.newOther("ProtoCloudEventData: getAny() was NULL");
|
||||
}
|
||||
|
||||
} else {
|
||||
if (Objects.equals(dataContentType, PROTO_DATA_CONTENT_TYPE)) {
|
||||
// This will throw if the data provided is not an Any. The protobuf CloudEvent spec requires proto data to be stored as
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue