Compare commits

...

160 Commits

Author SHA1 Message Date
Pierangelo Di Pilato 0d96691f6d
Migrate from OSSRH to Central Portal (#698)
Ref: https://central.sonatype.org/publish/publish-portal-maven/

Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2025-06-26 09:44:36 +02:00
Sleiman Jneidi c8a22b5175
build: upgrage to protobuf sdk 4.31.1 (#695)
Signed-off-by: Sleiman <sleiman.jneidi@apple.com>
Co-authored-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2025-06-20 14:56:34 +02:00
Sleiman Jneidi 57f9867a5c
build: remove maven cache (#696)
Signed-off-by: Sleiman <sleiman.jneidi@apple.com>
2025-06-20 09:43:41 +02:00
dependabot[bot] 43f0d5b138
Bump nokogiri from 1.16.5 to 1.18.3 in /docs (#685)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.16.5 to 1.18.3.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/v1.18.3/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.16.5...v1.18.3)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 08:18:56 +01:00
dependabot[bot] efe7e01b75
Bump org.apache.maven.plugins:maven-antrun-plugin from 3.0.0 to 3.1.0 (#682)
Bumps [org.apache.maven.plugins:maven-antrun-plugin](https://github.com/apache/maven-antrun-plugin) from 3.0.0 to 3.1.0.
- [Commits](https://github.com/apache/maven-antrun-plugin/compare/maven-antrun-plugin-3.0.0...maven-antrun-plugin-3.1.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-antrun-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-27 10:03:16 +01:00
dependabot[bot] 188c3c7085
Bump org.apache.qpid:proton-j from 0.33.7 to 0.34.1 (#678)
Bumps org.apache.qpid:proton-j from 0.33.7 to 0.34.1.

---
updated-dependencies:
- dependency-name: org.apache.qpid:proton-j
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-20 13:13:12 +01:00
dependabot[bot] 659bb8cc01
Bump org.apache.kafka:kafka-clients from 3.0.0 to 3.7.1 in /kafka (#681)
Bumps org.apache.kafka:kafka-clients from 3.0.0 to 3.7.1.

---
updated-dependencies:
- dependency-name: org.apache.kafka:kafka-clients
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-20 13:12:46 +01:00
Kristiyan Marinov 9981635e7d
fix: disallow empty 'subject' context attribute (#679)
Specification clearly states that 'subject' is optional but if present, MUST be non-empty
(spec at https://github.com/cloudevents/spec/blob/v1.0/spec.md#subject)

Signed-off-by: Kristiyan Marinov <kristiyanm@gmail.com>
2024-11-04 09:53:36 +01:00
dependabot[bot] b54df5ca0c
Bump io.projectreactor:reactor-core from 3.5.1 to 3.6.11 (#677)
Bumps [io.projectreactor:reactor-core](https://github.com/reactor/reactor-core) from 3.5.1 to 3.6.11.
- [Release notes](https://github.com/reactor/reactor-core/releases)
- [Commits](https://github.com/reactor/reactor-core/compare/v3.5.1...v3.6.11)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-30 06:47:37 +01:00
dependabot[bot] a3decf768c
Bump org.slf4j:slf4j-simple from 1.7.36 to 2.0.16 (#674)
Bumps org.slf4j:slf4j-simple from 1.7.36 to 2.0.16.

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-simple
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-29 08:01:59 +01:00
dependabot[bot] 215e67f2c3
Bump rexml from 3.3.6 to 3.3.9 in /docs (#675)
Bumps [rexml](https://github.com/ruby/rexml) from 3.3.6 to 3.3.9.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.3.6...v3.3.9)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-29 08:01:19 +01:00
dependabot[bot] 514e00d75b
Bump rexml from 3.2.8 to 3.3.6 in /docs (#665)
Bumps [rexml](https://github.com/ruby/rexml) from 3.2.8 to 3.3.6.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.2.8...v3.3.6)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-22 14:24:28 +02:00
dependabot[bot] f7ac215bf1
Bump org.eclipse.jetty:jetty-server in /examples/basic-http (#672)
Bumps org.eclipse.jetty:jetty-server from 9.4.51.v20230217 to 9.4.55.v20240627.

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-server
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-22 14:23:36 +02:00
dependabot[bot] 5f4abd144a
Bump org.apache.avro:avro from 1.11.3 to 1.11.4 in /formats/avro-compact (#671)
Bumps org.apache.avro:avro from 1.11.3 to 1.11.4.

---
updated-dependencies:
- dependency-name: org.apache.avro:avro
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 17:25:38 +02:00
github-actions[bot] 8d59d29bb1
Bump to 4.0.1 (#656)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: pierDipi <pierDipi@users.noreply.github.com>
2024-10-02 19:49:23 +02:00
Rohan Mallya 6ab42de007
docs: correcting import to EventFormatProvider (#658)
Signed-off-by: Rohan R Mallya <bangalorerohan@gmail.com>
2024-10-02 19:48:40 +02:00
dependabot[bot] 2e87988d0c
Bump org.apache.maven.plugins:maven-install-plugin from 2.5.1 to 3.1.3 (#664)
Bumps [org.apache.maven.plugins:maven-install-plugin](https://github.com/apache/maven-install-plugin) from 2.5.1 to 3.1.3.
- [Release notes](https://github.com/apache/maven-install-plugin/releases)
- [Commits](https://github.com/apache/maven-install-plugin/compare/maven-install-plugin-2.5.1...maven-install-plugin-3.1.3)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-install-plugin
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-02 19:48:09 +02:00
David Simansky 6d958b69d4
Fix cloudevents-sql artifactId in bom (#669)
Signed-off-by: David Simansky <dsimansk@redhat.com>
2024-10-02 19:47:43 +02:00
dependabot[bot] 1de03ad38a
Bump org.apache.rocketmq:rocketmq-client-java from 5.0.4 to 5.0.7 (#653)
Bumps [org.apache.rocketmq:rocketmq-client-java](https://github.com/apache/rocketmq-clients) from 5.0.4 to 5.0.7.
- [Release notes](https://github.com/apache/rocketmq-clients/releases)
- [Commits](https://github.com/apache/rocketmq-clients/compare/java-5.0.4...java-5.0.7)

---
updated-dependencies:
- dependency-name: org.apache.rocketmq:rocketmq-client-java
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 14:48:32 +02:00
Pierangelo Di Pilato 4c5d0efb89
Make CloudEventValidatorProvider thread safe (#650)
Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2024-07-02 17:39:05 +02:00
github-actions[bot] 01a9111d8b
Bump to 4.0.0 in docs (#649)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: pierDipi <pierDipi@users.noreply.github.com>
2024-07-01 20:29:40 +02:00
Pierangelo Di Pilato e056d1b2d8
Allow bumping versions in `/docs` only (#648)
This will allow changing the version in the documentation website
when a new release is published.

Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2024-07-01 17:24:48 +02:00
github-actions[bot] efdf0ba866
Bump to 4.1.0-SNAPSHOT (#645)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: pierDipi <pierDipi@users.noreply.github.com>
2024-07-01 12:10:47 +02:00
github-actions[bot] bd90d903ce
Bump to 4.0.0-SNAPSHOT (#644)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: pierDipi <pierDipi@users.noreply.github.com>
2024-07-01 11:41:33 +02:00
dependabot[bot] 52a98d778c
Bump rexml from 3.2.5 to 3.2.8 in /docs (#635)
Bumps [rexml](https://github.com/ruby/rexml) from 3.2.5 to 3.2.8.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.2.5...v3.2.8)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-01 10:59:57 +02:00
Calum Murray 010627e784
CE SQL v1 (#641)
- add exception factory for cesql exceptions
- extend EvaluationResult to be usable internally
- expressions use results instead of a thrower interface
- functions use results instead of a thrower interface
- parser handles not equals correctly, does not eagerly evaluate when there may be an error
- parser handles integer literals properly
- updated test files to test v1 spec

Signed-off-by: Calum Murray <cmurray@redhat.com>
Co-authored-by: Pierangelo Di Pilato <pierangelodipilato@gmail.com>
2024-06-21 17:28:26 +02:00
Juan Martinez a7904823c3
fix: invalid automatic module name (#639)
Inspired by [Automatic-Module-Name: Calling All Java Library Maintainers](https://dzone.com/articles/automatic-module-name-calling-all-java-library-maintainers), I added the module names for the non-specified modules on their respective `pom.xml` file

Signed-off-by: Juan MARTINEZ <Jummartinezro@users.noreply.github.com>
2024-06-21 09:24:33 +02:00
Mickaël Schoentgen e5a35ac472
doc: update & uniformize package version (#640)
Signed-off-by: Mickaël Schoentgen <Mickael.Schoentgen@hyland.com>
2024-06-20 12:08:07 +02:00
dependabot[bot] 4d204cef89
Bump org.xmlunit:xmlunit-core from 2.9.0 to 2.10.0 in /formats/xml (#631)
Bumps [org.xmlunit:xmlunit-core](https://github.com/xmlunit/xmlunit) from 2.9.0 to 2.10.0.
- [Release notes](https://github.com/xmlunit/xmlunit/releases)
- [Changelog](https://github.com/xmlunit/xmlunit/blob/main/RELEASE_NOTES.md)
- [Commits](https://github.com/xmlunit/xmlunit/compare/v2.9.0...v2.10.0)

---
updated-dependencies:
- dependency-name: org.xmlunit:xmlunit-core
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-14 11:04:06 +02:00
dependabot[bot] 43afb6ceaa
Bump nokogiri from 1.16.3 to 1.16.5 in /docs (#632)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.16.3 to 1.16.5.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.16.3...v1.16.5)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-14 11:03:01 +02:00
dependabot[bot] 9c6e7fd11e
Bump io.vertx:vertx-core from 4.3.7 to 4.5.3 in /http/vertx (#622)
Bumps [io.vertx:vertx-core](https://github.com/eclipse/vert.x) from 4.3.7 to 4.5.3.
- [Commits](https://github.com/eclipse/vert.x/compare/4.3.7...4.5.3)

---
updated-dependencies:
- dependency-name: io.vertx:vertx-core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-09 10:13:49 +02:00
github-actions[bot] 98deac1599
Bump to 3.1.0-SNAPSHOT (#613)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: pierDipi <pierDipi@users.noreply.github.com>
2024-03-26 09:59:22 +01:00
Pierangelo Di Pilato 05baf9be8a
Run workflows on any major version branch (#615)
Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2024-03-26 08:42:45 +01:00
Pierangelo Di Pilato 3add823e00
Run workflows on any major version branch (#614)
Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2024-03-26 08:39:35 +01:00
dependabot[bot] 5a03173dde
Bump org.apache.maven.plugins:maven-source-plugin from 2.2.1 to 3.3.0 (#608)
Bumps [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) from 2.2.1 to 3.3.0.
- [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-2.2.1...maven-source-plugin-3.3.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-source-plugin
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-26 08:12:40 +01:00
dependabot[bot] 9ee16fb48c
Bump nokogiri from 1.13.10 to 1.16.3 in /docs (#609)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.10 to 1.16.3.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.10...v1.16.3)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-26 08:11:52 +01:00
dependabot[bot] 0db4446163
Bump com.github.hanleyt:jersey-junit from 2.1.0 to 2.2.0 (#584)
Bumps [com.github.hanleyt:jersey-junit](https://github.com/hanleyt/jersey-junit) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/hanleyt/jersey-junit/releases)
- [Commits](https://github.com/hanleyt/jersey-junit/compare/2.1.0...2.2.0)

---
updated-dependencies:
- dependency-name: com.github.hanleyt:jersey-junit
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2024-03-11 15:57:25 +01:00
touchkey 111fb55cfd
feat: Customizing Cloudevents validation (#594)
Add SPI for custom CloudEvent validation.

Signed-off-by: vbhat6 <vinayas_bhat@intuit.com>
2024-02-15 07:48:55 +01:00
Matej Vasek 55fddb35fc
feat: JSON format to assume JSON content-type where possible (#604)
Signed-off-by: Matej Vašek <mvasek@redhat.com>
2024-02-08 09:49:19 +01:00
Calum Murray 7b9d020acc
Update cesql TCK tests (#603)
Signed-off-by: Calum Murray <cmurray@redhat.com>
2024-01-31 11:51:03 +01:00
Doug Davis fb11b94f2b
Add link to CloudEvent security mailing list (#599)
Signed-off-by: Doug Davis <dug@microsoft.com>
2023-10-16 15:26:53 +02:00
Boris Stumm eaef3becdd
#588 Update jackson to 2.15.2 (#589)
Signed-off-by: Boris Stumm <bs@boris-stumm.de>
2023-10-02 10:08:48 +02:00
dependabot[bot] b30324e916
Bump org.apache.avro:avro from 1.11.2 to 1.11.3 in /formats/avro-compact (#593)
Bumps org.apache.avro:avro from 1.11.2 to 1.11.3.

---
updated-dependencies:
- dependency-name: org.apache.avro:avro
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 10:08:22 +02:00
Doug Davis 1f9fa13231
Align SDK governance docs (#590)
Related to https://github.com/cloudevents/spec/pull/1226

Signed-off-by: Doug Davis <dug@microsoft.com>
2023-09-28 09:29:59 +02:00
dependabot[bot] a135755ec6
Bump jackson-dataformat-yaml from 2.11.2 to 2.15.2 (#577)
Bumps [jackson-dataformat-yaml](https://github.com/FasterXML/jackson-dataformats-text) from 2.11.2 to 2.15.2.
- [Commits](https://github.com/FasterXML/jackson-dataformats-text/compare/jackson-dataformats-text-2.11.2...jackson-dataformats-text-2.15.2)

---
updated-dependencies:
- dependency-name: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-21 09:19:35 +02:00
dependabot[bot] 677a2c2628
Bump jetty-server in /examples/basic-http (#579)
Bumps [jetty-server](https://github.com/eclipse/jetty.project) from 9.4.41.v20210516 to 9.4.51.v20230217.
- [Release notes](https://github.com/eclipse/jetty.project/releases)
- [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.41.v20210516...jetty-9.4.51.v20230217)

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-server
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-21 09:19:06 +02:00
dependabot[bot] 826e099fc0
Bump io.openliberty.arquillian:arquillian-liberty-managed-jakarta-junit (#582)
Bumps [io.openliberty.arquillian:arquillian-liberty-managed-jakarta-junit](https://github.com/OpenLiberty/arquillian-liberty-dependencies) from 2.1.0 to 2.1.4.
- [Release notes](https://github.com/OpenLiberty/arquillian-liberty-dependencies/releases)
- [Commits](https://github.com/OpenLiberty/arquillian-liberty-dependencies/compare/arquillian-liberty-jakarta-dependencies-2.1.0...arquillian-liberty-jakarta-dependencies-2.1.4)

---
updated-dependencies:
- dependency-name: io.openliberty.arquillian:arquillian-liberty-managed-jakarta-junit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-21 09:18:34 +02:00
dependabot[bot] 76366338fc
Bump truth-proto-extension from 1.1 to 1.1.5 (#580)
Bumps truth-proto-extension from 1.1 to 1.1.5.

---
updated-dependencies:
- dependency-name: com.google.truth.extensions:truth-proto-extension
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-20 18:03:41 +02:00
Alex Collins 4ef304115a
[working-draft] Support Avro Compact Format (#578)
Add support for working-draft spec Avro compact format: 777d0c0398

Signed-off-by: Alex Collins <alex_collins@intuit.com>
2023-07-20 18:02:59 +02:00
dependabot[bot] 582feed520
Bump maven-gpg-plugin from 1.6 to 3.1.0 (#564)
Bumps [maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 1.6 to 3.1.0.
- [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-1.6...maven-gpg-plugin-3.1.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-gpg-plugin
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 12:08:54 +02:00
github-actions[bot] 5ef1088a19
Bump to 3.0.0-SNAPSHOT (#571)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: pierDipi <pierDipi@users.noreply.github.com>
2023-05-23 10:55:49 +02:00
Aaron Ai 698cdf7ad4
feat: add rocketmq binding (#554)
Spec details: a6978cf562/rocketmq-cloudevents-binding/rocketmq-transport-binding.md

Signed-off-by: Aaron Ai <yangkun.ayk@alibaba-inc.com>
2023-05-23 10:49:08 +02:00
skepticoitusInteruptus 4ebeab0e0f
Refactor to Facilitate Decoupling from Concrete Implementations of EventFormat (#539)
- Introduce ContentType enum
- Resolve formats by using the ContentType enum

Signed-off-by: Randi Sheaffer-Klass <97033958+skepticoitusInteruptus@users.noreply.github.com>
2023-04-21 10:41:10 +02:00
Jem Day 4c81f3eacc
Make ProtoCloudEventData consistent (#535)
Modified ProtoCloudEventData to always return a 
Protobuf Any object - this ensures it is coherent with
the Protobuf CloudEvent format specification.

It remains possible to wrap any Protobuf 'Message'
object directly (which includes an 'Any') as a
convienience to reduce application code.

Signed-off-by: Jem Day <Jem.Day@cliffhanger.com>
2023-03-10 09:35:24 +01:00
Pierangelo Di Pilato 569e025cf0
Fix javadoc build (#529) (#534)
Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2023-03-06 18:25:53 +01:00
Jem Day 3614a4f5f4
Fixed Protobuf data corruption for CloudEvent serialized/deserialized several times (#524)
Fixed issue where mutiple serialize/de-serialize operations
would result in corrupted data if the data was a protobuf
message object.

- Introduced equality checks for ProtoDataWrapper.
- Refactored and cleaned up data-wrappers.

Fixes https://github.com/cloudevents/sdk-java/issues/523

Signed-off-by: Jem Day <Jem.Day@cliffhanger.com>
2023-02-27 11:48:07 +01:00
mxsm d64aff7327
[#521] Remove unused imports (#522)
Signed-off-by: mxsm <ljbmxsm@gmail.com>
2023-02-20 18:06:28 +01:00
dependabot[bot] d1cff75230
Bump activesupport from 6.0.6 to 6.0.6.1 in /docs (#517)
Bumps [activesupport](https://github.com/rails/rails) from 6.0.6 to 6.0.6.1.
- [Release notes](https://github.com/rails/rails/releases)
- [Changelog](https://github.com/rails/rails/blob/v7.0.4.2/activesupport/CHANGELOG.md)
- [Commits](https://github.com/rails/rails/compare/v6.0.6...v6.0.6.1)

---
updated-dependencies:
- dependency-name: activesupport
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-20 08:42:50 +01:00
Ruben Romero Montes 1591cb337a
[#465] Upgrade Quarkus examples with binary and structured events (#515)
Signed-off-by: ruromero <rromerom@redhat.com>
2023-01-24 16:54:22 +01:00
Pierangelo Di Pilato f71303b7b7
Switch default branch to main (#506)
Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2023-01-12 19:03:51 +01:00
Pierangelo Di Pilato d59b33307a
Run tests before deploy on Java 17 (#504)
Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2023-01-10 16:49:47 +01:00
dependabot[bot] e0d1961f35
Bump nokogiri from 1.13.9 to 1.13.10 in /docs (#496)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.9 to 1.13.10.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.9...v1.13.10)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-10 16:41:29 +01:00
dependabot[bot] 7c6b52ab30
Bump reactor-core from 3.4.21 to 3.5.1 (#499)
Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.21 to 3.5.1.
- [Release notes](https://github.com/reactor/reactor-core/releases)
- [Commits](https://github.com/reactor/reactor-core/compare/v3.4.21...v3.5.1)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-10 16:10:42 +01:00
Jem Day 433ec5b274
Initial Implementation of XML Format (#448)
Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>
Signed-off-by: Jem Day <Jem.Day@cliffhanger.com>
2023-01-10 09:48:36 +01:00
Doug Davis 40fe91a5e0
Use UTF-8 when using getBytes (#491)
Closes #488

Signed-off-by: Doug Davis <dug@microsoft.com>
2023-01-05 18:01:14 +01:00
Pierangelo Di Pilato a43f90f4e2
Create dependabot.yml 2023-01-02 15:19:06 +01:00
dependabot[bot] e488269510
Bump nokogiri from 1.13.6 to 1.13.9 in /docs (#486)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.6 to 1.13.9.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.6...v1.13.9)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-02 15:11:24 +01:00
Fabio José 7d50c7fc7a
Bump Vertx to 4.3.7 (#495)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-3167773

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2023-01-02 15:10:38 +01:00
Averi Kitsch f1a86af656
Fix curl command for Spring example (#478)
Signed-off-by: Averi Kitsch <akitsch@google.com>
2023-01-02 13:36:30 +01:00
Doug Davis 354f7a16ef
Upgrade kafka, protobuf, and vert.x versions (#492)
Closes #489

Signed-off-by: Doug Davis <dug@microsoft.com>
2022-12-09 12:35:19 +01:00
Pierangelo Di Pilato 9132a13d81
Remove deprecated constructor usage for JsonCloudEventData (#483)
Fixes #482

Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2022-10-12 08:58:41 +02:00
dependabot[bot] a491c85eb2
Bump tzinfo from 1.2.8 to 1.2.10 in /docs (#467)
Bumps [tzinfo](https://github.com/tzinfo/tzinfo) from 1.2.8 to 1.2.10.
- [Release notes](https://github.com/tzinfo/tzinfo/releases)
- [Changelog](https://github.com/tzinfo/tzinfo/blob/master/CHANGES.md)
- [Commits](https://github.com/tzinfo/tzinfo/compare/v1.2.8...v1.2.10)

---
updated-dependencies:
- dependency-name: tzinfo
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-12 08:56:14 +02:00
Frédéric Déléchamp 6362bfbcd6
Strip parameters from data content types to assess if it's JSON format (#484)
Signed-off-by: Frederic Delechamp <fdelechamp@guidewire.com>
2022-10-06 10:42:37 +02:00
Alex Butcher f08a099ed9
Restful-ws jakarta ee9 namespace support (#469)
* Replace javax restful-ws namespace usage with jakarata in own module
* Add microprofile with openliberty example using new jar
* Update gitignore to cover all copied directories
* Update Jersey Version to support jakarta.* namespace packages
* Port jersey integration tests from javax.* to jakarta.*
* Undo changes to existing integration test package names
* Add Integration test for Microprofile/Liberty for Jee9 package
* Add documentation for new Jakarta package

Signed-off-by: alex-butcher <21243172+abutch3r@users.noreply.github.com>
2022-09-27 09:16:03 +02:00
Vikram Vuppla 4139fb7e57
Fix typo in word Covert (#480)
Signed-off-by: Vikram Vuppla <naga.vicky@gmail.com>
2022-09-19 17:31:31 +02:00
Gerard Klijs adde53c817
Correct URL to naming-conventions (#477)
Signed-off-by: Gerard Klijs <gerard.klijs@axoniq.io>
2022-09-14 15:46:09 +02:00
Jorge Oliva 0dc10251ff
Fix example in "Materialize an Extension" (#475)
Was using `ExtensionParser` instead `ExtensionProvider`

Signed-off-by: Jorge Oliva <Jorge.OlivaFernandez@santander.co.uk>
2022-09-14 11:39:25 +02:00
github-actions[bot] b9eaa2fcaa
Bump to 2.5.0-SNAPSHOT (#474)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: pierDipi <pierdipi@redhat.com>
2022-09-06 15:52:34 +02:00
Isaac Aymerich f8d27b08bf
Support dynamic JSON data content type (#471)
Now checks if `datacontenttype` matches the regex:

`^(application|text)\/([a-zA-Z]+\+)?json$")`

This regex support 
`application/foobar+json`
or standard
```
application/json
text/json
```

Signed-off-by: Isaac Aymerich <isaac.aymerich@roche.com>
2022-09-05 13:14:38 +02:00
Matej Vasek 9125136530
chore: update Quarkus example (#466)
* Server accepts both: binary and structured encoded events.
* Emitter contains comment describing how to switch between binary and
  structured encoding for emitted events.

Signed-off-by: Matej Vasek <mvasek@redhat.com>
2022-07-19 12:27:44 +02:00
Gustavo Tedesco f35e6e610a
CVE-2020-36518 - bump jackson from 2.11.2 to 2.13.3 (#464)
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-36518

Signed-off-by: Gustavo Tedesco <gustavo.tedesco@unico.io>
2022-07-13 17:16:38 +02:00
Pierangelo Di Pilato 45ec85f8c1
Optimize `isCloudEventsHeader` check (#445)
Instead of using:
```java
key.substring(0, CE_PREFIX.length()).toLowerCase().startsWith(CE_PREFIX);
```
we can just use:
```java
key.regionMatches(true /* ignoreCase */, 0, CE_PREFIX, 0, CE_PREFIX.length());
```

Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2022-07-13 10:18:17 +02:00
dependabot[bot] 1d87fb7191
Bump nokogiri from 1.13.3 to 1.13.6 in /docs (#457)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.3 to 1.13.6.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.3...v1.13.6)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-25 16:06:16 +02:00
Snyk bot 8f9b741306
[Snyk] Security upgrade io.vertx:vertx-core from 4.0.0 to 4.2.5 (#455)
* fix: http/vertx/pom.xml to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1020439
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1070799
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1082234
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1082235
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1082236
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1083991
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1089809
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1317097
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1584063
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-1584064
- https://snyk.io/vuln/SNYK-JAVA-IONETTY-2314893

* Bump Vert.x to 4.2.5

Co-authored-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2022-04-08 11:13:07 +02:00
dependabot[bot] 2dd8ba95dd
Bump nokogiri from 1.12.5 to 1.13.3 in /docs (#447)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.12.5 to 1.13.3.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.12.5...v1.13.3)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-01 19:01:24 +02:00
Pierangelo Di Pilato 1c29726e8c
Add `CONTRIBUTING.md` and `MAINTAINERS.md` files (#454)
These 2 new files are based on the existing `pr_guidelines.md`
and `maintainers_guide.md`

Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
2022-04-01 19:00:37 +02:00
mEstrazulas 6681205733
Fix Spring structured example (#451)
Signed-off-by: Micael Vianna <estrazulas@gmail.com>
Signed-off-by: Micael Estrazulas Vianna <m.vianna@kigroup.de>
2022-03-18 23:24:37 +01:00
Jem Day a4bc7a8368
Protobuf: Enhance textual content-types detection (#444)
- Move content-type introspection in a separate support class.
- Add unit tests to ensure introspection is correct.

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>
2022-01-31 10:18:22 +01:00
github-actions[bot] 4784f03e8c
Bump to 2.4.0-SNAPSHOT (#439)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: pierDipi <pierdipi@redhat.com>
2021-12-21 12:18:07 +01:00
Pierangelo Di Pilato ace6859ae0
Bump version to 2.3.0 (#437)
Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
2021-12-21 12:06:57 +01:00
Pierangelo Di Pilato cc786251d5
Handle NullNode for optional attributes in Jackson CloudEventDeserializer (#432)
In `getOptionalStringNode` we should handle `JsonNode`s that are
instances of `NullNode`.

Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
2021-12-21 11:22:02 +01:00
Myeonghyeon-Lee ceb06757a3
Ignore invalid extension names in jackson CloudEventDeserializer (#429)
Signed-off-by: mhyeon-lee <mhyeon.lee@navercorp.com>
2021-12-10 10:26:47 +01:00
Pierangelo Di Pilato 8d91cdaee6
Run tests with Java 17 (#426)
Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
2021-12-01 09:05:02 +01:00
Pierangelo Di Pilato d00ad967c0
Fix the Java Doc build (#424)
- https://vertx.io/docs/apidocs/ returns 404, so removing it
- Fix Java Doc error for missing `@param`

Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
2021-11-17 16:55:27 +01:00
Dave Syer 9231e6d230
Add example of Spring WebClient usage (#418)
- Add example of WebClient usage
- Add additional test case with WebClient usage

Signed-off-by: Dave Syer <dsyer@vmware.com>
2021-11-17 16:25:59 +01:00
Joke de Buhr a94bc5c81c
Close input stream on CloudEventHttpMessageReader (#421)
Signed-off-by: Joke de Buhr <joke.debuhr@eventim.de>
2021-11-03 09:54:38 +01:00
Dmitrii Bocharov 32adfe9123
Fix NPE in CloudEventDeserializer when deserializing header with null value (#415)
Signed-off-by: Dmitrii Bocharov <dmitrii.bocharov@embedit.cz>
2021-11-03 08:28:54 +01:00
dependabot[bot] 0277ee4ae4
Bump spring-framework-bom in /examples/restful-ws-spring-boot (#423)
Bumps [spring-framework-bom](https://github.com/spring-projects/spring-framework) from 5.2.8.RELEASE to 5.2.9.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v5.2.8.RELEASE...v5.2.9.RELEASE)

---
updated-dependencies:
- dependency-name: org.springframework:spring-framework-bom
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-03 08:26:35 +01:00
dependabot[bot] 202849307c
Bump addressable from 2.7.0 to 2.8.0 in /docs (#406)
Bumps [addressable](https://github.com/sporkmonger/addressable) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/sporkmonger/addressable/releases)
- [Changelog](https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sporkmonger/addressable/compare/addressable-2.7.0...addressable-2.8.0)

---
updated-dependencies:
- dependency-name: addressable
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-03 08:07:20 +01:00
dependabot[bot] 624ac693d8
Bump nokogiri from 1.11.5 to 1.12.5 in /docs (#419)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.5 to 1.12.5.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.5...v1.12.5)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-03 08:06:43 +01:00
dependabot[bot] 5a323942d3
Bump jetty-server in /examples/basic-http (#403)
Bumps [jetty-server](https://github.com/eclipse/jetty.project) from 9.4.38.v20210224 to 9.4.41.v20210516.
- [Release notes](https://github.com/eclipse/jetty.project/releases)
- [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.38.v20210224...jetty-9.4.41.v20210516)

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-server
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-05 12:22:52 +02:00
dependabot[bot] 1587708805
Bump spring-framework-bom (#382)
Bumps [spring-framework-bom](https://github.com/spring-projects/spring-framework) from 5.2.8.RELEASE to 5.2.9.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v5.2.8.RELEASE...v5.2.9.RELEASE)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-05 12:22:46 +02:00
Francesco Guardiani 06c4ec5385
Moved exception factory methods internally (#402)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-07-05 11:43:46 +02:00
Nicolas Vervelle 73a3c370d5
build: provide a Bill of Materials artifact for easier integration in projects (#405)
Signed-off-by: Nicolas Vervelle <nicolas.vervelle@quicksign.com>
2021-06-28 09:30:41 +02:00
github-actions[bot] 722f5205b3
Bump to 2.3.0-SNAPSHOT (#400)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2021-06-17 12:18:55 +02:00
github-actions[bot] 8ad857d8c7
Bump to 2.2.0 (#399)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2021-06-17 12:05:04 +02:00
Francesco Guardiani baa9b5927a
[CESQL] Reorganize package implementation classes (#397)
* Move classes around to improve package organization

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Removed bad imports

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-06-15 10:31:14 +02:00
Francesco Guardiani c41a2c3ba7
Cleanup CloudEventUtils (#398)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-06-14 16:30:07 +02:00
Francesco Guardiani 3651cdae18
Add a method to simplify casting (#396)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-06-07 08:36:02 +02:00
dependabot[bot] ee4c85b1a1
Bump nokogiri from 1.11.1 to 1.11.5 in /docs (#393)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.1 to 1.11.5.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.1...v1.11.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-04 09:41:26 +02:00
Francesco Guardiani a0b0835180
[CESQL] Constant folding (#392)
* Added visitor for the expressions

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* First constant folding draft

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Constant folding for unary expressions

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* More testing
Constant folding for exists expression

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Little mistake

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Added a ParserBuilder

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-06-04 09:19:38 +02:00
Pei-Tang Huang 687e03bac5
Update the link to http4k Cloud Events module. (#394)
Signed-off-by: Pei-Tang Huang <tangtheone@gmail.com>
2021-05-26 09:36:30 +02:00
Francesco Guardiani 208b18c299
Build release branches too (#386)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-05-06 14:07:52 +02:00
Francesco Guardiani 9d45943844
Fix like expression (#381)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-05-03 10:05:26 +02:00
shnplr 0a1a03db64
fix: Update encoding for structured event (#384)
Signed-off-by: Paul Strachan <paul.strachan@det.nsw.edu.au>
2021-05-03 09:39:48 +02:00
dependabot[bot] 7f355d10c1
Bump rexml from 3.2.4 to 3.2.5 in /docs (#383)
Bumps [rexml](https://github.com/ruby/rexml) from 3.2.4 to 3.2.5.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.2.4...v3.2.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-03 09:34:47 +02:00
Francesco Guardiani 3234e30e55
CESQL Benchmark (#380)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-04-30 11:01:02 +02:00
github-actions[bot] c8f10e9215
Bump to 2.2.0-SNAPSHOT (#378)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2021-04-28 15:50:41 +02:00
github-actions[bot] ba9ccad5d2
Bump to 2.1.0 (#377)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2021-04-28 15:24:41 +02:00
Francesco Guardiani 78355bb225
Fix #326 (#376)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-04-28 15:15:45 +02:00
Francesco Guardiani 2730ae4a13
Expression language (#363)
* Configured the sql package

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Bootstrap implementation

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Literal done

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* More progress

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Progress

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Sync contract

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Fix type cohercion for event type system

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* In expression + sync grammar

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Implemented binary expressions

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Implemented Like expression

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Big refactor

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* More testing
Fix math

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Implemented all the functions!

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Refactored logical expressions implementation
More testing

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* More coverage and tests

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Fixed ConcatFunction and added ConcatWSFunction

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Fixed IN type casting

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Added ABS function

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Fix SUBSTRING implementation

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* More nits

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* WIP Javadoc-ing

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Fix division by 0

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Bootstrapped TCK

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Added comparison operators to tck

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Added logical operators, case sensitivity and casting functions

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Copied all the tests to the tck

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Removed Java tests now covered by the TCK

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Added integer builtin test case

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Added fail fast evaluation mode

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* More changes
More Javadoc

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Typo

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Fix bad javadoc

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Another CONCAT_WS test case

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Import yaml just for testing

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2021-04-28 14:58:55 +02:00
Johan Haleby 30fd6769eb
fix: Adding withoutData, withoutDataContentType and withoutDataSchema to CloudEventBuilder (#374)
Signed-off-by: Johan Haleby <johan.haleby@gmail.com>
2021-04-23 13:59:39 +02:00
dependabot[bot] ff07dd8315
Bump jetty-server in /examples/basic-http (#372)
Bumps [jetty-server](https://github.com/eclipse/jetty.project) from 9.4.35.v20201120 to 9.4.38.v20210224.
- [Release notes](https://github.com/eclipse/jetty.project/releases)
- [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.35.v20201120...jetty-9.4.38.v20210224)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 12:38:51 +02:00
Erik Paulson 928ebcfd6f
formats: Add support for protobuf format (#348)
* formats: Add support for protobuf format

Adds support for the Protocol Buffer CloudEvent format defined at
https://github.com/cloudevents/spec/blob/v1.0.1/protobuf-format.md.

Compiles the Proto3 file taken from the spec repo into generated Java
protobuf classes. These classes are used to convert the SDK
representation of a CloudEvent to and from the protobuf format.

Signed-off-by: Erik Paulson <epaulson@apexclearing.com>

* Address feedback in PR #348

- Adds service file for event format autoloading
- Addresses some field access issues
- Treats unset fields as omitted
- Updates and adds documentation

Signed-off-by: Erik Paulson <epaulson@apexclearing.com>

* Add missing attribute writer methods for CloudEventBuilders

Without these methods, binary attributes are interpreted as
Strings instead of byte[].

Signed-off-by: Erik Paulson <epaulson@apexclearing.com>

* Added test data files.

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* - Now executes tests related to wire-format files.
- Supports V03 dialect

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* - Added new ProtoCloudEventData construct to support proto message based data.
- Added some more test files.

- When the PR related to binary context attributes is merged we can extend the
test use-cases appropriately.

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* - Merged changes related to binary context attributes.
- Modified proto format to process binary context attributes
- Added CloudEventData varient to hold proto messages (requires tests)
- Will add further tests once the failing test is addressed

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* Added test for protobuf data

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* Indicate that v0.3 events are supported by Protobuf Format

Even though the protobuf spec came out for v1, the attributes are
fairly easily mapped back to v0.3.

Signed-off-by: Erik Paulson <epaulson@apexclearing.com>

* Add missing comments; fix formatting; minor refactoring

Signed-off-by: Erik Paulson <epaulson@apexclearing.com>

* Create a full Protobuf CloudEventWriter

Converts the ProtoContextWriter class to a ProtoCloudEventWriter class
and modifies the format code to use it instead of manually writing
data to the output.

Signed-off-by: Erik Paulson <epaulson@apexclearing.com>

* - Addressed Review Comments.
- Introduced a default ProtoDataWrapper
- Tests updated.

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* Address Review Comments

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* Test cleanup and timezone testing

This does some tweaking to tests by moving to using assertj and fixing
whitespace. It also adds a new test to ensure that timezones are handled
correctly.

Signed-off-by: Erik Paulson <epaulson@apexclearing.com>

* Formatting cleanup

- Remove unecessary whitespace
- Fix Javadocs
- Delete unused code

Signed-off-by: Erik Paulson <epaulson@apexclearing.com>

Co-authored-by: Day, Jeremy(jday) <jday@paypal.com>
2021-04-19 09:24:26 +02:00
Pierangelo Di Pilato a4613c00d2
Avoid allocating an array on extension validation (#367)
Signed-off-by: Pierangelo Di Pilato <pierangelodipilato@gmail.com>
2021-04-06 10:14:08 +02:00
Pierangelo Di Pilato 69f0e20549
CloudEvents attribute names SHOULD NOT exceed 20 chars (#366)
As per spec [1], attribute names SHOULD NOT and not MUST NOT
exceed 20 characters in length.

[1] https://github.com/cloudevents/spec/blob/master/spec.md#attribute-naming-conventionthis

Signed-off-by: Pierangelo Di Pilato <pierangelodipilato@gmail.com>
2021-04-06 10:00:55 +02:00
Jem Day e2b13109e4
Throw exceptions when atempting to handling unsupport CloudEvent formats. (#362)
- Modfy logic that selects between structured and binary modes during reception.
- Introduced new test scenarios to veify behavior.

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>
2021-03-26 09:55:23 +01:00
Jem Day 5e3bfc890f
Specification Compliant handling of numeric context attributes (#358)
* - Added tests case to verify expected handling of numeric context attributes
- Updated serializer.

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* - Added @deprecated marker for CloudEventContextWriter.set(name, Number)
- Added use of new method for JSON serializer.

Cleanup of deprecated implementations can occur independantly.

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* Addressed Review Comments

- Now throws exception when non specification compliant numeric
  attribute values are received during deserialization.

- Added test cases to verify deserialization exceptions.

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* Address Review Comments

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* Address Review Comment

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>
2021-03-24 16:58:33 +01:00
Jem Day 13f8b56618
Introduced support for Binary attribute types. (#353)
* Introduced support for Binary attribute types.

Added test data example
Added unit-test for JSON Format

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* documentation tweak

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* - Addressed review comment.
- Removed the withContextAttribute(string, Integer).
   - This should be a seperate PR, was mixed-in by accident.

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>

* Address review comments

Signed-off-by: Day, Jeremy(jday) <jday@paypal.com>
2021-03-10 11:29:38 +01:00
Dave Syer a419d8bba3
Add Spring Cloud Function sample (#356)
* Add Spring Cloud Function sample

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Fix example curl command with structured event

Signed-off-by: Dave Syer <dsyer@vmware.com>
2021-03-08 15:08:16 +01:00
Dave Syer 47bed5616d
Flesh out the docs in the Spring module a bit (#355)
Signed-off-by: Dave Syer <dsyer@vmware.com>
2021-03-08 14:39:17 +01:00
Dave Syer baba37ccfd
Regularize copyright headers (#354)
Signed-off-by: Dave Syer <dsyer@vmware.com>
2021-03-05 10:19:47 +01:00
dependabot[bot] 32bcdcd3b9
Bump nokogiri from 1.10.10 to 1.11.1 in /docs (#347)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.10.10 to 1.11.1.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.10.10...v1.11.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-05 09:18:32 +01:00
Dave Syer 8b382734d9
Add support for RSockets with Spring (#349)
* Add support for RSockets with Spring

Also generically can support structured events with any Spring
API that works with Encoder and Decoder. There's a sample for
the RSocket case with a simple request-response echo server.

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Use supported mime types from format provider

Signed-off-by: Dave Syer <dsyer@vmware.com>
2021-03-05 09:18:19 +01:00
Dave Syer f05418cba9
Allow user to enumerate supported content types (#350)
Signed-off-by: Dave Syer <dsyer@vmware.com>
2021-03-01 11:02:54 +01:00
David Denton 23cd08fcfd
Fix #344 - add http4k references to docs (#345)
Signed-off-by: David Denton <denton.david@gmail.com>
2021-02-15 09:42:13 +01:00
github-actions[bot] 70782da2c2
Bump to 2.1.0-SNAPSHOT (#346)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2021-02-15 09:19:58 +01:00
github-actions[bot] 48fc69e058
Bump to 2.0.0 (#343)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2021-02-15 09:09:09 +01:00
Mark Scott e7e6e46bd5
fix: prevent NPE on deserializing JSON containing invalid `specversion` value (#342)
* fix: prevent NPE on deserializing JSON containing invalid `specversion` value

Signed-off-by: Mark Scott <mark@codebrewer.org>

* refactor: move test per PR review comment

Signed-off-by: Mark Scott <mark@codebrewer.org>
2021-02-03 08:30:29 +01:00
Francesco Guardiani e523bfbfbf
Rename extension (#339)
* Fixed broken links

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Renamed Extension to CloudEventExtension

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2021-02-02 08:32:43 +01:00
Thomas Qvarnström 12eee4da6e
Use scheduler instead of StartupEvent. Fixes #332 (#333)
Signed-off-by: Thomas Qvarnström <tqvarnst@redhat.com>
2020-12-16 11:36:28 +01:00
Alfusainey Jallow d49ff9f69d
update AMQP docs (#331)
Signed-off-by: Alfusainey Jallow <alf.jallow@gmail.com>
2020-12-14 11:08:41 +01:00
github-actions[bot] 611f2292a7
Bump to 2.0.0-SNAPSHOT (#329)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2020-12-11 11:15:02 +01:00
github-actions[bot] 296230719b
Bump to 2.0.0.RC2 (#327)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2020-12-11 11:04:09 +01:00
Francesco Guardiani d9592d5201
Fixed broken links (#328)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

Co-authored-by: slinkydeveloper <slinkydeveloper@users.noreply.github.com>
2020-12-11 10:57:40 +01:00
Francesco Guardiani 00cdf9cb42
Jackson javadocs (#319)
* Jackson javadocs

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Reverted public constructor and deprecated it

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2020-12-11 10:45:21 +01:00
Francesco Guardiani bd11010138
Kafka javadocs (#321)
* Kafka javadocs

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Nit

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2020-12-11 10:42:43 +01:00
Francesco Guardiani 3a22557b83
Website docs (#324)
* Halfway through it

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* That should be it

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Prettier run

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Removed code sample

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Suggestions + fixed up the mess made by prettier

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Suggestion

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2020-12-11 10:38:04 +01:00
Francesco Guardiani 87c6915d9a
AMQP javadocs (#322)
* Javadocs

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Removed createReader for structured mode and refactored createReader(String, ApplicationProperties, byte[]) to createReader(String, ApplicationProperties, Section)

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2020-12-11 08:41:40 +01:00
Francesco Guardiani a7f87cf6cb
Javadocs to http mods (#320)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2020-12-10 19:28:02 +01:00
Francesco Guardiani 711277eacb
Vert.x 4! (#325)
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2020-12-10 17:51:45 +01:00
Francesco Guardiani 58570cf4d9
Spring Javadocs (#323)
* Spring Javadocs

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Rebase fix

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2020-12-10 17:15:07 +01:00
Dave Syer 24d108fe5d
Improve CloudEventHeaderUtils with narrower scoped method (#318)
Signed-off-by: Dave Syer <dsyer@vmware.com>
2020-12-10 10:25:21 +01:00
Francesco Guardiani f5d9b47c1c
Javadocs api and core (#313)
* Javadocs!!!

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Missing module name?

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Excluding javadocs

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* clean install only release artifacts, but verify them all!

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Reverted the crazy idea to use the release profile

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Suggestions

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Suggestion

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Nit

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Nit

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Nit

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2020-12-09 18:31:56 +01:00
Dave Syer 59643c3368
Add support for Message<CloudEvent> (#315)
* Add support for Message<CloudEvent>

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Add support for structured messages with Spring Message<?>

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Push private classes out to shared utilities

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Resolve some more review comments

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Restructure MessageReader and MessageWriter

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Remove integration test (depends on snapshots still)

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Simplify message converter but drop support for structured format

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Make HTTP optional

Signed-off-by: Dave Syer <dsyer@vmware.com>

* Drop snapshot reporitory declarations

Signed-off-by: Dave Syer <dsyer@vmware.com>
2020-12-09 18:12:09 +01:00
Alfusainey Jallow 34483025df
Fix 404 Not Found in AMQP module Readme (#316)
Signed-off-by: Alfusainey Jallow <alf.jallow@gmail.com>
2020-12-02 21:28:14 +01:00
dependabot[bot] e9d15daf28
Bump jetty-server in /examples/basic-http (#317)
Bumps [jetty-server](https://github.com/eclipse/jetty.project) from 9.4.30.v20200611 to 9.4.35.v20201120.
- [Release notes](https://github.com/eclipse/jetty.project/releases)
- [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.30.v20200611...jetty-9.4.35.v20201120)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-02 19:54:54 +01:00
Francesco Guardiani b89f45265b
Simplify the Reader/Writer implementations, reducing the knowledge of spec details (#309)
* Messing up stuff

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Collapse CloudEventAttributesWriter and CloudEventAttributesWriter into CloudEventContextWriter

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Rebase fix

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
2020-12-01 17:27:50 +01:00
Dave Syer a14f5eabec
Add a Spring sample with webflux (#314)
Signed-off-by: Dave Syer <dsyer@vmware.com>
2020-11-30 18:16:20 +01:00
Dave Syer 5099b31f6c
HTTP converters for CloudEvent in Spring (#312)
Supports MVC and WebFlux (blocking and non-blocking) HTTP.
User can work with `CloudEvent` as a `POJO` type and inject it
into `@ReqestMapping` methods.

Signed-off-by: Dave Syer <dsyer@vmware.com>

Co-authored-by: Oleg Zhurakousky <ozhurakousky@pivotal.io>
2020-11-30 11:27:44 +01:00
426 changed files with 19161 additions and 1659 deletions

11
.github/dependabot.yml vendored Normal file
View File

@ -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"

View File

@ -4,6 +4,8 @@ on:
push:
branches:
- master
- main
- '[0-9]+.[0-9]+'
jobs:
test:
@ -11,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
@ -19,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
@ -31,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 }}

View File

@ -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" {} +'

View File

@ -4,20 +4,24 @@ on:
pull_request:
branches:
- master
- main
- '[0-9]+.[0-9]+'
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ 8, 11 ]
java: [ 8, 11, 17 ]
name: Java ${{ matrix.java }} Test
steps:
- uses: actions/checkout@v2
- name: Setup java
uses: actions/setup-java@v1
uses: actions/setup-java@v2
with:
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

5
.gitignore vendored
View File

@ -42,3 +42,8 @@ _site/
.sass-cache/
.jekyll-cache/
.jekyll-metadata
# MacOS
*.DS_Store
/http/restful-ws-jakarta/src/main/*

View File

@ -1,4 +1,19 @@
# Pull Request Guidelines
# Contributing to CloudEvents' Java SDK
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
We welcome contributions from the community! Please take some time to become
acquainted with the process before submitting a pull request. There are just
a few things to keep in mind.
# Pull Requests
Typically, a pull request should relate to an existing issue. If you have
found a bug, want to add an improvement, or suggest an API change, please
create an issue before proceeding with a pull request. For very minor changes
such as typos in the documentation this isn't really necessary.
## Pull Request Guidelines
Here you will find step by step guidance for creating, submitting and updating
a pull request in this repository. We hope it will help you have an easy time
@ -8,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)
@ -32,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
```
@ -87,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`.
@ -116,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
```
@ -141,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.

5
MAINTAINERS.md Normal file
View File

@ -0,0 +1,5 @@
# Maintainers
Current active maintainers of this SDK:
- [Pierangelo Di Pilato](https://github.com/pierDipi)

View File

@ -1,17 +1,5 @@
# 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.
@ -38,14 +26,14 @@ When landing pull requests, be sure to check the first line uses an appropriate
## Branch Management
The `master` branch is the bleeding edge. New major versions of the module
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 `master HEAD` as your starting point.
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 `master` for the next major version - e.g. 2.0.0. Version branches
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.

View File

@ -4,10 +4,20 @@
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.cloudevents/cloudevents-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.cloudevents/cloudevents-parent)
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-core.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-core)
A Java API for the
[CloudEvents specification](https://github.com/cloudevents/spec)
The Java SDK for CloudEvents is a collection of Java packages to adopt
[CloudEvents](https://github.com/cloudevents/spec) in your Java application.
Look at https://cloudevents.github.io/sdk-java/ for more documentation.
Using the Java SDK you can:
- Access, create and manipulate `CloudEvent` inside your application.
- Serialize and deserialize `CloudEvent` back and forth using the _CloudEvents
Event Format_, like Json.
- Read and write `CloudEvent` back and forth to HTTP, Kafka, AMQP using the
_CloudEvents Protocol Binding_ implementations we provide for a wide range
of well known Java frameworks/libraries.
To check out the complete documentation and how to get started, look at the dedicated website
https://cloudevents.github.io/sdk-java/.
## Status
@ -32,46 +42,45 @@ Supported features of the specification:
| - [Vert.x](http/vertx) | :heavy_check_mark: | :heavy_check_mark: |
| - [Jakarta Restful WS](http/restful-ws) | :heavy_check_mark: | :heavy_check_mark: |
| - [Basic](http/basic) | :heavy_check_mark: | :heavy_check_mark: |
| - [Spring](spring) | :heavy_check_mark: | :heavy_check_mark: |
| - [http4k][http4k]<sup></sup>| :heavy_check_mark: | :heavy_check_mark: |
| JSON Event Format | :heavy_check_mark: | :heavy_check_mark: |
| - [Jackson](formats/json-jackson) | :heavy_check_mark: | :heavy_check_mark: |
| Protobuf Event Format | :heavy_check_mark: | :heavy_check_mark: |
| - [Proto](formats/protobuf) | :heavy_check_mark: | :heavy_check_mark: |
| [Kafka Protocol Binding](kafka) | :heavy_check_mark: | :heavy_check_mark: |
| MQTT Protocol Binding | :x: | :x: |
| NATS Protocol Binding | :x: | :x: |
| Web hook | :x: | :x: |
## Motivation
The [CloudEvents specification](https://github.com/cloudevents/spec) is a
vendor-neutral specification for defining the format of event data that is being
exchanged between different cloud systems. The specification basically defines
an abstract envelope for any event data payload, without knowing specific
implementation details of the actual underlying event. The current version of
the spec is at `1.0` and it describes a simple event format, which was
demonstrated at [KubeCon 2018](https://youtu.be/TZPPjAv12KU) using different
_Serverless platforms_, such as
[Apache Openwhisk](https://github.com/apache/incubator-openwhisk).
<sub>† Source/artifacts hosted externally</sub>
## Documentation
Documentation is available at https://cloudevents.github.io/sdk-java/
Documentation is available at https://cloudevents.github.io/sdk-java/.
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)
- [cloudevents-kafka](https://www.javadoc.io/doc/io.cloudevents/cloudevents-kafka)
- [cloudevents-amqp](https://www.javadoc.io/doc/io.cloudevents/cloudevents-amqp)
- [cloudevents-spring](https://www.javadoc.io/doc/io.cloudevents/cloudevents-spring)
You can check out the examples in the [examples](examples) directory.
## Used By
| [Occurrent](https://occurrent.org) | [Knative Eventing](https://github.com/knative-sandbox/eventing-kafka-broker) |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <a href="https://occurrent.org"><img src="https://raw.githubusercontent.com/johanhaleby/occurrent/master/occurrent-logo-196x196.png" width="98" height="98" alt="Occurrent" title="Occurrent - Event Sourcing Utilities for the JVM"></img></a> | <a href="https://github.com/knative-sandbox/eventing-kafka-broker"><img src="https://cloudevents.io/img/logos/integrations/knative.png" height="98"></img></a> |
| [Occurrent](https://occurrent.org) | [Knative Eventing](https://github.com/knative-sandbox/eventing-kafka-broker )| [http4k][http4k] |
| ---------------------------------- | ---------------------------------------------------------------------------- | ---------------|
| <a href="https://occurrent.org"><img src="https://raw.githubusercontent.com/johanhaleby/occurrent/master/occurrent-logo-196x196.png" width="98" height="98" alt="Occurrent" title="Occurrent - Event Sourcing Utilities for the JVM"></img></a> | <a href="https://github.com/knative-sandbox/eventing-kafka-broker"><img src="https://cloudevents.io/img/logos/integrations/knative.png" height="98"></img></a> | <a href="https://www.http4k.org/guide/modules/cloud_events/"><img src="https://http4k.org/img/favicon-310.png" height="98" alt="http4k" title="http4k"></img></a> | |
## Community
@ -90,11 +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/

12
RELEASING.md Normal file
View File

@ -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.

View File

@ -1,35 +1,5 @@
# AMQP Protocol Binding
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-amqp-proton.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-amqp-proton)
Javadoc: [![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-amqp-proton.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-amqp-proton)
This module implements `MessageReader` and `MessageWriter` using the Qpid Proton library. It can be used with Qpid Proton or any integrations based on Qpid Proton (e.g vertx-proton).
For Maven based projects, use the following to configure the `proton` AMQP binding for CloudEvents:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-amqp-proton</artifactId>
<version>2.0.0.RC1</version>
</dependency>
```
## Sending and Receiving CloudEvents
To send and receive CloudEvents we use `MessageWriter` and `MessageReader`, respectively.
This module offers factory methods for creation of those in `ProtonAmqpMessageFactory`.
```java
public class ProtonAmqpMessageFactory {
public static MessageReader createReader(final Message message);
public static MessageReader createReader(final String contentType, final byte[] payload);
public static MessageReader createReader(final String contentType, final ApplicationProperties props, final byte[] payload);
public static MessageWriter createWriter();
}
```
Examples:
The example uses the vertx-proton integration to send/receive CloudEvent messages over AMQP.
* [Vertx AmqpServer](../../examples/amqp-proton/src/main/java/io/cloudevents/examples/amqp/vertx/AmqpServer.java)
* [Vertx AmqpClient](../../examples/amqp-proton/src/main/java/io/cloudevents/examples/amqp/vertx/AmqpClient.java)
Documentation: https://cloudevents.github.io/sdk-java/amqp-proton

View File

@ -6,7 +6,7 @@
<parent>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</parent>
<artifactId>cloudevents-amqp-proton</artifactId>
@ -14,10 +14,12 @@
<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>
<dependencies>
<dependencies>
<dependency>
<groupId>org.apache.qpid</groupId>
<artifactId>proton-j</artifactId>

View File

@ -17,7 +17,7 @@
package io.cloudevents.amqp;
import io.cloudevents.SpecVersion;
import io.cloudevents.amqp.impl.AmqpConstants;
import io.cloudevents.amqp.impl.ProtonAmqpBinaryMessageReader;
import io.cloudevents.amqp.impl.ProtonAmqpMessageWriter;
@ -25,13 +25,17 @@ import io.cloudevents.core.message.MessageReader;
import io.cloudevents.core.message.MessageWriter;
import io.cloudevents.core.message.impl.GenericStructuredMessageReader;
import io.cloudevents.core.message.impl.MessageUtils;
import io.cloudevents.lang.Nullable;
import io.cloudevents.rw.CloudEventRWException;
import io.cloudevents.rw.CloudEventWriter;
import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
import org.apache.qpid.proton.amqp.messaging.Section;
import org.apache.qpid.proton.message.Message;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* A factory class providing convenience methods for creating MessageReader and MessageWriter instances based on Qpid Proton.
* A factory class providing convenience methods for creating {@link MessageReader} and {@link MessageWriter} instances based on Qpid Proton {@link Message}.
*/
@ParametersAreNonnullByDefault
public final class ProtonAmqpMessageFactory {
@ -41,62 +45,47 @@ public final class ProtonAmqpMessageFactory {
}
/**
* Creates a MessageReader to read a proton-based {@link Message}.
* <p>
* This implementation simply calls {@link #createReader(String, ApplicationProperties, byte[])}.
* Creates a {@link MessageReader} to read a proton-based {@link Message}.
* This reader is able to read both binary and structured encoded {@link io.cloudevents.CloudEvent}.
*
* @param message The proton message to read from.
*
* @return A message reader that can read the given proton message to a cloud event representation.
* @param message The proton {@link Message} to read from.
* @return A {@link MessageReader} that can read the given proton {@link Message} to a {@link io.cloudevents.CloudEvent} representation.
* @throws CloudEventRWException if something goes wrong while resolving the {@link SpecVersion} or if the message has unknown encoding
* @see #createReader(String, ApplicationProperties, Section)
*/
public static MessageReader createReader(final Message message) {
final byte[] payload = AmqpConstants.getPayloadAsByteArray(message.getBody());
return createReader(message.getContentType(), message.getApplicationProperties(), payload);
public static MessageReader createReader(final Message message) throws CloudEventRWException {
return createReader(message.getContentType(), message.getApplicationProperties(), message.getBody());
}
/**
* Creates a MessageReader using the content-type property and payload of a proton-based message.
* <p>
* This method simply calls {@link #createReader(String, ApplicationProperties, byte[])}.
* Creates a MessageReader to read using the {@code content-type} property, {@code application-properties} and data payload
* of a proton-based {@link Message}. This reader is able to read both binary and structured encoded {@link io.cloudevents.CloudEvent}.
*
* @param contentType The content-type of the message payload.
* @param payload The message payload in bytes.
* @return A message reader capable of representing a CloudEvent from
* a message <em>content-type</em> property and <em>application-data</em>.
* @param contentType The {@code content-type} of the message payload.
* @param props The {@code application-properties} section of the proton-message containing cloud event metadata (attributes and/or extensions).
* @param body The message body or {@code null} if the message does not contain any body.
* @return A {@link MessageReader} capable of representing a {@link io.cloudevents.CloudEvent} from the {@code application-properties},
* {@code content-type} and payload of a proton message.
* @throws CloudEventRWException if something goes wrong while resolving the {@link SpecVersion} or if the message has unknown encoding
*/
public static MessageReader createReader(final String contentType, final byte[] payload) {
return createReader(contentType, null, payload);
public static MessageReader createReader(final String contentType, final ApplicationProperties props, @Nullable final Section body) throws CloudEventRWException {
final byte[] payload = AmqpConstants.getPayloadAsByteArray(body);
return MessageUtils.parseStructuredOrBinaryMessage(
() -> contentType,
format -> new GenericStructuredMessageReader(format, payload),
() -> AmqpConstants.getApplicationProperty(props, AmqpConstants.APP_PROPERTY_SPEC_VERSION, String.class),
sv -> new ProtonAmqpBinaryMessageReader(sv, props, contentType, payload)
);
}
/**
* Creates a MessageWriter capable of translating both a structured and binary CloudEvent
* to a proton-based AMQP 1.0 representation.
* Creates a {@link MessageWriter} capable of translating both a structured and binary CloudEvent
* to a proton-based AMQP 1.0 {@link Message}.
*
* @return A message writer to read structured and binary cloud event from a proton-based message.
* @return A {@link MessageWriter} to write a {@link io.cloudevents.CloudEvent} to Proton {@link Message} using structured or binary encoding.
*/
public static MessageWriter<CloudEventWriter<Message>, Message> createWriter() {
return new ProtonAmqpMessageWriter<>();
}
/**
* Creates a MessageReader to read using the content-type property, application-propeties and data payload
* of a proton-based message.
*
* @param contentType The content-type of the message payload.
* @param props The application-properties section of the proton-message containing cloud event metadata (attributes and/or extensions).
* @param payload The message payload in bytes or {@code null} if the message does not contain any payload.
* @return A message reader capable of representing a CloudEvent from the application-properties,
* content-type and payload of a proton message.
*/
public static MessageReader createReader(final String contentType, final ApplicationProperties props, final byte[] payload) {
return MessageUtils.parseStructuredOrBinaryMessage(
() -> contentType,
format -> new GenericStructuredMessageReader(format, payload),
() -> AmqpConstants.getApplicationProperty(props, AmqpConstants.APP_PROPERTY_SPEC_VERSION, String.class),
sv -> new ProtonAmqpBinaryMessageReader(sv, props, contentType, payload));
}
}

View File

@ -17,22 +17,20 @@
package io.cloudevents.amqp.impl;
import java.util.HashMap;
import org.apache.qpid.proton.amqp.Binary;
import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
import org.apache.qpid.proton.amqp.messaging.Data;
import org.apache.qpid.proton.message.Message;
import io.cloudevents.CloudEventData;
import io.cloudevents.SpecVersion;
import io.cloudevents.core.format.EventFormat;
import io.cloudevents.core.message.MessageWriter;
import io.cloudevents.core.v1.CloudEventV1;
import io.cloudevents.rw.CloudEventAttributesWriter;
import io.cloudevents.rw.CloudEventExtensionsWriter;
import io.cloudevents.rw.CloudEventContextWriter;
import io.cloudevents.rw.CloudEventRWException;
import io.cloudevents.rw.CloudEventWriter;
import org.apache.qpid.proton.amqp.Binary;
import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
import org.apache.qpid.proton.amqp.messaging.Data;
import org.apache.qpid.proton.message.Message;
import java.util.HashMap;
/**
* A proton-based MessageWriter capable of writing both structured and binary CloudEvent messages to an AMQP 1.0 representation as
@ -53,33 +51,28 @@ public final class ProtonAmqpMessageWriter<R> implements MessageWriter<CloudEven
}
@Override
public CloudEventAttributesWriter withAttribute(final String name, final String value) throws CloudEventRWException {
public CloudEventContextWriter withContextAttribute(String name, String value) throws CloudEventRWException {
if (name.equals(CloudEventV1.DATACONTENTTYPE)) {
message.setContentType(value);
} else {
// for now, extensions are mapped to application-properties
// see https://github.com/cloudevents/sdk-java/issues/30#issuecomment-723982190
if (applicationProperties == null) {
throw new IllegalStateException("This Writer is not initialized");
}
applicationProperties.getValue().put(AmqpConstants.ATTRIBUTES_TO_PROPERTYNAMES.get(name), value);
String propName = AmqpConstants.ATTRIBUTES_TO_PROPERTYNAMES.get(name);
if (propName == null) {
propName = name;
}
applicationProperties.getValue().put(propName, value);
}
return null;
}
@Override
public CloudEventExtensionsWriter withExtension(final String name, final String value) throws CloudEventRWException {
// for now, extensions are mapped to application-properties
// see https://github.com/cloudevents/sdk-java/issues/30#issuecomment-723982190
if (applicationProperties == null) {
throw new IllegalStateException("This Writer is not initialized");
}
applicationProperties.getValue().put(name, value);
return null;
return this;
}
@Override
public ProtonAmqpMessageWriter<R> create(final SpecVersion version) {
if (applicationProperties == null) {
applicationProperties = new ApplicationProperties(new HashMap<String, Object>());
applicationProperties = new ApplicationProperties(new HashMap<>());
}
applicationProperties.getValue().put(AmqpConstants.APP_PROPERTY_SPEC_VERSION, version.toString());
return this;
@ -105,5 +98,4 @@ public final class ProtonAmqpMessageWriter<R> implements MessageWriter<CloudEven
message.setApplicationProperties(applicationProperties);
return message;
}
}

View File

@ -17,18 +17,6 @@
package io.cloudevents.amqp;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import io.cloudevents.CloudEvent;
import io.cloudevents.SpecVersion;
import io.cloudevents.amqp.impl.AmqpConstants;
@ -39,6 +27,19 @@ import io.cloudevents.core.test.Data;
import io.cloudevents.core.v03.CloudEventV03;
import io.cloudevents.core.v1.CloudEventV1;
import io.cloudevents.types.Time;
import org.apache.qpid.proton.amqp.Binary;
import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
import org.apache.qpid.proton.amqp.messaging.Section;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests verifying the behavior of the {@code ProtonAmqpMessageFactory}.
@ -53,7 +54,8 @@ public class ProtonAmqpMessageFactoryTest {
@MethodSource("binaryTestArguments")
public void readBinary(final Map<String, Object> props, final String contentType, final byte[] body,
final CloudEvent event) {
final MessageReader amqpReader = ProtonAmqpMessageFactory.createReader(contentType, new ApplicationProperties(props), body);
final Section bodySection = body != null ? new org.apache.qpid.proton.amqp.messaging.Data(new Binary(body)) : null;
final MessageReader amqpReader = ProtonAmqpMessageFactory.createReader(contentType, new ApplicationProperties(props), bodySection);
assertThat(amqpReader.getEncoding()).isEqualTo(Encoding.BINARY);
assertThat(amqpReader.toEvent()).isEqualTo(event);
}
@ -64,7 +66,7 @@ public class ProtonAmqpMessageFactoryTest {
final String contentType = CSVFormat.INSTANCE.serializedContentType() + "; charset=utf8";
final byte[] contentPayload = CSVFormat.INSTANCE.serialize(event);
final MessageReader amqpReader = ProtonAmqpMessageFactory.createReader(contentType, null, contentPayload);
final MessageReader amqpReader = ProtonAmqpMessageFactory.createReader(contentType, null, new org.apache.qpid.proton.amqp.messaging.Data(new Binary(contentPayload)));
assertThat(amqpReader.getEncoding()).isEqualTo(Encoding.STRUCTURED);
assertThat(amqpReader.toEvent()).isEqualTo(event);
}

View File

@ -1,28 +1,5 @@
# CloudEvents API
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-api.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-api)
Javadoc: [![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-api.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-api)
For Maven based projects, use the following dependency:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-api</artifactId>
<version>2.0.0.RC1</version>
</dependency>
```
## Features
This package provides the base interfaces used by the SDK. In particular:
- `CloudEvent` is the main interface representing a read only CloudEvent in-memory representation
- `Extension` represents a _materialized_ in-memory representation of a CloudEvent extension
- `SpecVersion` is an enum of CloudEvents' specification versions supported by this SDK version.
- `CloudEventVisitor`/`CloudEventVisitable` are the interfaces used by the SDK to implement protocol bindings/event formats.
A 3rd party implementer can implement these interfaces directly in its `CloudEvent` in order
to customize/implement efficiently the marshalling/unmarshalling process.
These interfaces are optional and, if your `CloudEvent` doesn't implement it, a default implementation is provided by the SDK.
`cloudevents-core` provides the implementation of these interfaces, and a 3rd party implementer can grab this package
to implement specialized CloudEvent in-memory representations.
Documentation: https://cloudevents.github.io/sdk-java/api

View File

@ -24,7 +24,7 @@
<parent>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</parent>
<artifactId>cloudevents-api</artifactId>

View File

@ -20,12 +20,12 @@ import io.cloudevents.lang.Nullable;
/**
* Interface representing an in memory read only representation of a CloudEvent,
* as specified by the <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md">CloudEvents specification</a>
* as specified by the <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md">CloudEvents specification</a>.
*/
public interface CloudEvent extends CloudEventContext {
/**
* The event data
* @return The event data, if any, otherwise {@code null}
*/
@Nullable
CloudEventData getData();

View File

@ -23,10 +23,10 @@ import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Set;
/**
* Materialized CloudEvent Extension interface to read/write the extension attributes key/values.
* Materialized CloudEvent extension interface to read/write the extension attributes key/values.
*/
@ParametersAreNonnullByDefault
public interface Extension {
public interface CloudEventExtension {
/**
* Fill this materialized extension with values from a {@link CloudEventExtensions} implementation.
@ -39,7 +39,7 @@ public interface Extension {
* Get the attribute of extension named {@code key}.
*
* @param key the name of the extension attribute
* @return the extension value in one of the valid types String/Number/Boolean
* @return the extension value in one of the valid types String/Number/Boolean/URI/OffsetDateTime/byte[]
* @throws IllegalArgumentException if the key is unknown to this extension
*/
@Nullable

View File

@ -29,11 +29,17 @@ import java.util.stream.Stream;
*/
@ParametersAreNonnullByDefault
public enum SpecVersion {
/**
* @see <a href="https://github.com/cloudevents/spec/releases/tag/v0.3">CloudEvents release v0.3</a>
*/
V03(
"0.3",
Arrays.asList("specversion", "id", "type", "source"),
Arrays.asList("datacontenttype", "datacontentencoding", "schemaurl", "subject", "time")
),
/**
* @see <a href="https://github.com/cloudevents/spec/releases/tag/v1.0">CloudEvents release v1.0</a>
*/
V1(
"1.0",
Arrays.asList("specversion", "id", "type", "source"),

View File

@ -27,6 +27,9 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static javax.annotation.meta.When.MAYBE;
/**
* This annotation is used to define a method parameter or return type as nullable.
*/
@Target(value = {METHOD, PARAMETER, FIELD})
@Retention(value = RUNTIME)
@Documented

View File

@ -1,65 +0,0 @@
/*
* 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.rw;
import io.cloudevents.types.Time;
import java.net.URI;
import java.time.OffsetDateTime;
/**
* Interface to write the attributes from a {@link io.cloudevents.rw.CloudEventReader} to a new representation.
*/
public interface CloudEventAttributesWriter {
/**
* Set attribute with type {@link String}. This setter should not be invoked for specversion, because the built Visitor already
* has the information through the {@link CloudEventWriterFactory}.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this attribute.
*/
CloudEventAttributesWriter withAttribute(String name, String value) throws CloudEventRWException;
/**
* Set attribute with type {@link URI}.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this attribute.
*/
default CloudEventAttributesWriter withAttribute(String name, URI value) throws CloudEventRWException {
return withAttribute(name, value == null ? null : value.toString());
}
/**
* Set attribute with type {@link OffsetDateTime} attribute.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this attribute.
*/
default CloudEventAttributesWriter withAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
return withAttribute(name, value == null ? null : Time.writeTime(name, value));
}
}

View File

@ -28,19 +28,11 @@ import javax.annotation.ParametersAreNonnullByDefault;
public interface CloudEventContextReader {
/**
* Visit self attributes using the provided writer
* Read the context attributes and extensions using the provided writer
*
* @param writer Attributes writer
* @throws CloudEventRWException if something went wrong during the visit.
* @param writer context writer
* @throws CloudEventRWException if something went wrong during the read.
*/
void readAttributes(CloudEventAttributesWriter writer) throws CloudEventRWException;
/**
* Visit self extensions using the provided writer
*
* @param visitor Extensions writer
* @throws CloudEventRWException if something went wrong during the visit.
*/
void readExtensions(CloudEventExtensionsWriter visitor) throws CloudEventRWException;
void readContext(CloudEventContextWriter writer) throws CloudEventRWException;
}

View File

@ -0,0 +1,138 @@
/*
* 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.rw;
import io.cloudevents.types.Time;
import javax.annotation.ParametersAreNonnullByDefault;
import java.net.URI;
import java.time.OffsetDateTime;
import java.util.Base64;
/**
* Interface to write the context attributes/extensions from a {@link io.cloudevents.rw.CloudEventContextReader} to a new representation.
*/
@ParametersAreNonnullByDefault
public interface CloudEventContextWriter {
/**
* Set attribute with type {@link String}.
* This setter should not be invoked for specversion, because the writer should
* already know the specversion or because it doesn't need it to correctly write the value.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this attribute.
* @throws IllegalArgumentException if you're trying to set the specversion attribute.
*/
CloudEventContextWriter withContextAttribute(String name, String value) throws CloudEventRWException;
/**
* Set attribute with type {@link URI}.
* This setter should not be invoked for specversion, because the writer should
* already know the specversion or because it doesn't need it to correctly write the value.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this attribute.
* @throws IllegalArgumentException if you're trying to set the specversion attribute.
*/
default CloudEventContextWriter withContextAttribute(String name, URI value) throws CloudEventRWException {
return withContextAttribute(name, value.toString());
}
/**
* Set attribute with type {@link OffsetDateTime} attribute.
* This setter should not be invoked for specversion, because the writer should
* already know the specversion or because it doesn't need it to correctly write the value.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this attribute.
* @throws IllegalArgumentException if you're trying to set the specversion attribute.
*/
default CloudEventContextWriter withContextAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
return withContextAttribute(name, Time.writeTime(name, value));
}
/**
* Set attribute with type {@link Number}.
* This setter should not be invoked for specversion, because the writer should
* already know the specversion or because it doesn't need it to correctly write the value.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this extension.
* @throws IllegalArgumentException if you're trying to set the specversion attribute.
*
* @deprecated CloudEvent specification only permits {@link Integer} type as a
* numeric value.
*/
default CloudEventContextWriter withContextAttribute(String name, Number value) throws CloudEventRWException {
return withContextAttribute(name, value.toString());
}
/**
* Set attribute with type {@link Integer}.
* This setter should not be invoked for specversion, because the writer should
* already know the specversion or because it doesn't need it to correctly write the value.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this extension.
* @throws IllegalArgumentException if you're trying to set the specversion attribute.
*/
default CloudEventContextWriter withContextAttribute(String name, Integer value) throws CloudEventRWException {
return withContextAttribute(name, value.toString());
}
/**
* Set attribute with type {@link Boolean} attribute.
* This setter should not be invoked for specversion, because the writer should
* already know the specversion or because it doesn't need it to correctly write the value.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this extension.
* @throws IllegalArgumentException if you're trying to set the specversion attribute.
*/
default CloudEventContextWriter withContextAttribute(String name, Boolean value) throws CloudEventRWException {
return withContextAttribute(name, value.toString());
}
/**
* Set attribute with a binary type.
* This setter should not be invoked for specversion, because the writer should
* already know the specversion or because it doesn't need it to correctly write the value.
*
* @param name name of the attribute
* @param value value of the attribute
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this extension.
* @throws IllegalArgumentException if you're trying to set the specversion attribute.
*/
default CloudEventContextWriter withContextAttribute(String name, byte[] value) throws CloudEventRWException {
return withContextAttribute(name, Base64.getEncoder().encodeToString(value));
}
}

View File

@ -23,6 +23,8 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface to convert a {@link CloudEventData} instance to another one.
*
* @param <R> the returned {@link CloudEventData} from this mapper.
*/
@FunctionalInterface
@ParametersAreNonnullByDefault
@ -38,7 +40,7 @@ public interface CloudEventDataMapper<R extends CloudEventData> {
R map(CloudEventData data) throws CloudEventRWException;
/**
* No-op identity mapper which can be used as default when no mapper is provided.
* @return No-op identity mapper which can be used as default when no mapper is provided.
*/
static CloudEventDataMapper<CloudEventData> identity() {
return d -> d;

View File

@ -1,61 +0,0 @@
/*
* 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.rw;
import java.net.URI;
/**
* Interface to write the extensions from a {@link io.cloudevents.rw.CloudEventReader} to a new representation.
*/
public interface CloudEventExtensionsWriter {
/**
* Set an extension with type {@link String}.
*
* @param name name of the extension
* @param value value of the extension
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this extension.
*/
CloudEventExtensionsWriter withExtension(String name, String value) throws CloudEventRWException;
/**
* Set attribute with type {@link URI}.
*
* @param name name of the extension
* @param value value of the extension
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this extension.
*/
default CloudEventExtensionsWriter withExtension(String name, Number value) throws CloudEventRWException {
return withExtension(name, value == null ? null : value.toString());
}
/**
* Set attribute with type {@link Boolean} attribute.
*
* @param name name of the extension
* @param value value of the extension
* @return self
* @throws CloudEventRWException if anything goes wrong while writing this extension.
*/
default CloudEventExtensionsWriter withExtension(String name, Boolean value) throws CloudEventRWException {
return withExtension(name, value == null ? null : value.toString());
}
}

View File

@ -17,6 +17,8 @@
package io.cloudevents.rw;
import io.cloudevents.lang.Nullable;
/**
* This class is the exception Protocol Binding and Event Format implementers can use to signal errors while serializing/deserializing CloudEvent.
*/
@ -35,9 +37,9 @@ public class CloudEventRWException extends RuntimeException {
*/
INVALID_ATTRIBUTE_NAME,
/**
* The extension name is not valid,
* because it doesn't follow the <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#attribute-naming-convention">naming convention</a>
* enforced by the CloudEvents spec.
* The extension name is not valid because it doesn't follow the naming convention enforced by the CloudEvents spec.
*
* @see <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#attribute-naming-convention">naming convention</a>
*/
INVALID_EXTENSION_NAME,
/**
@ -83,10 +85,17 @@ public class CloudEventRWException extends RuntimeException {
this.kind = kind;
}
/**
* @return the {@link CloudEventRWExceptionKind} associated to this exception instance.
*/
public CloudEventRWExceptionKind getKind() {
return kind;
}
/**
* @param specVersion the invalid input spec version
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidSpecVersion(String specVersion) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_SPEC_VERSION,
@ -94,6 +103,10 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param attributeName the invalid attribute name
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidAttributeName(String attributeName) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_ATTRIBUTE_NAME,
@ -101,6 +114,10 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param extensionName the invalid extension name
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidExtensionName(String extensionName) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_EXTENSION_NAME,
@ -108,6 +125,11 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param attributeName the invalid attribute name
* @param clazz the type of the attribute
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidAttributeType(String attributeName, Class<?> clazz) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_ATTRIBUTE_TYPE,
@ -115,7 +137,24 @@ public class CloudEventRWException extends RuntimeException {
);
}
public static CloudEventRWException newInvalidAttributeValue(String attributeName, Object value, Throwable cause) {
public static CloudEventRWException newInvalidAttributeType(String attributeName,Object value){
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_ATTRIBUTE_TYPE,
"Invalid attribute/extension type for \""
+ attributeName
+ "\": Type" + value.getClass().getCanonicalName()
+ ". Value: " + value
);
}
/**
* @param attributeName the invalid attribute name
* @param value the value of the attribute
* @param cause an optional cause identifying the eventual validation error
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidAttributeValue(String attributeName, Object value, @Nullable Throwable cause) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_ATTRIBUTE_VALUE,
"Invalid attribute/extension value for \"" + attributeName + "\": " + value,
@ -123,6 +162,11 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param actual the actual data type
* @param allowed the list of allowed data types
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidDataType(String actual, String... allowed) {
String message;
if (allowed.length == 0) {
@ -136,6 +180,12 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param cause the cause of the conversion failure
* @param from the input data type
* @param to the target data type
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newDataConversion(Throwable cause, String from, String to) {
return new CloudEventRWException(
CloudEventRWExceptionKind.DATA_CONVERSION,
@ -144,6 +194,22 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @return a new {@link CloudEventRWException} instance.
*/
public static CloudEventRWException newUnknownEncodingException() {
return new CloudEventRWException(
CloudEventRWExceptionKind.UNKNOWN_ENCODING,
"Could not parse. Unknown encoding. Invalid content type or spec version"
);
}
/**
* This wraps a {@link Throwable} in a new generic instance of this exception.
*
* @param cause the cause of the exception
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newOther(Throwable cause) {
return new CloudEventRWException(
CloudEventRWExceptionKind.OTHER,
@ -151,10 +217,14 @@ public class CloudEventRWException extends RuntimeException {
);
}
public static CloudEventRWException newUnknownEncodingException() {
return new CloudEventRWException(
CloudEventRWExceptionKind.UNKNOWN_ENCODING,
"Could not parse. Unknown encoding. Invalid content type or spec version"
);
/**
* 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);
}
}

View File

@ -30,18 +30,29 @@ import javax.annotation.ParametersAreNonnullByDefault;
public interface CloudEventReader {
/**
* Visit self using the provided visitor factory
* Like {@link #read(CloudEventWriterFactory, CloudEventDataMapper)}, but with the identity {@link CloudEventDataMapper}.
*
* @param <W> The type of the {@link CloudEventWriter} created by writerFactory
* @param <R> The return value of the {@link CloudEventWriter} created by writerFactory
* @param writerFactory a factory that generates a visitor starting from the SpecVersion of the event
* @see #read(CloudEventWriterFactory, CloudEventDataMapper)
* @return the value returned by {@link CloudEventWriter#end()} or {@link CloudEventWriter#end(CloudEventData)}
* @throws CloudEventRWException if something went wrong during the read.
*/
default <V extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<V, R> writerFactory) throws CloudEventRWException {
default <W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory) throws CloudEventRWException {
return read(writerFactory, CloudEventDataMapper.identity());
}
/**
* Like {@link CloudEventReader#read(CloudEventWriterFactory)}, but providing a mapper for {@link io.cloudevents.CloudEventData} to be invoked when the data field is available.
* Read self using the provided writer factory.
*
* @param <W> the {@link CloudEventWriter} type
* @param <R> the return type of the {@link CloudEventWriter}
* @param writerFactory a factory that generates a visitor starting from the SpecVersion of the event
* @param mapper the mapper to invoke when building the {@link CloudEventData}
* @return the value returned by {@link CloudEventWriter#end()} or {@link CloudEventWriter#end(CloudEventData)}
* @throws CloudEventRWException if something went wrong during the read.
*/
<V extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<V, R> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException;
<W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException;
}

View File

@ -25,18 +25,19 @@ import io.cloudevents.CloudEventData;
*
* @param <R> return value at the end of the write process
*/
public interface CloudEventWriter<R> extends CloudEventAttributesWriter, CloudEventExtensionsWriter {
public interface CloudEventWriter<R> extends CloudEventContextWriter {
/**
* End the visit with a data field
* End the write with a data payload.
*
* @param data the data to write
* @return an eventual return value
* @throws CloudEventRWException if the message writer cannot be ended.
*/
R end(CloudEventData data) throws CloudEventRWException;
/**
* End the visit
* End the write.
*
* @return an eventual return value
* @throws CloudEventRWException if the message writer cannot be ended.

View File

@ -19,8 +19,14 @@ package io.cloudevents.rw;
import io.cloudevents.SpecVersion;
/**
* This factory is used to enforce setting the {@link SpecVersion} as first step in the writing process.
*
* @param <W> The type of the {@link CloudEventWriter} created by this factory
* @param <R> The return value of the {@link CloudEventWriter} created by this factory
*/
@FunctionalInterface
public interface CloudEventWriterFactory<V extends CloudEventWriter<R>, R> {
public interface CloudEventWriterFactory<W extends CloudEventWriter<R>, R> {
/**
* Create a {@link CloudEventWriter} starting from the provided {@link SpecVersion}
@ -29,5 +35,5 @@ public interface CloudEventWriterFactory<V extends CloudEventWriter<R>, R> {
* @return the new writer
* @throws CloudEventRWException if the spec version is invalid or the writer cannot be instantiated.
*/
V create(SpecVersion version) throws CloudEventRWException;
W create(SpecVersion version) throws CloudEventRWException;
}

View File

@ -21,7 +21,7 @@
<parent>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</parent>
<artifactId>cloudevents-benchmarks</artifactId>
@ -34,6 +34,7 @@
<javac.target>1.8</javac.target>
<!-- Name of the benchmark Uber-JAR to generate. -->
<uberjar.name>benchmarks</uberjar.name>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
<dependencies>
@ -58,6 +59,11 @@
<artifactId>cloudevents-kafka</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-sql</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
@ -135,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>
@ -155,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>

View File

@ -0,0 +1,35 @@
package io.cloudevents.bench.sql;
import io.cloudevents.sql.Parser;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.infra.Blackhole;
public class CompileBenchmark {
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
public void testCompileSmallExpression(Blackhole bh) {
bh.consume(
Parser.parseDefault("(a + b + c) = 10 AND TRUE OR CONCAT('1', '2', id) = '123'")
);
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
public void testCompileSmallTrueConstantExpression(Blackhole bh) {
bh.consume(
Parser.parseDefault("(1 + 2 + 3) = 10 AND TRUE OR CONCAT('1', '2', '3') = 123")
);
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
public void testCompileSmallFalseConstantExpression(Blackhole bh) {
bh.consume(
Parser.parseDefault("(1 + 2 + 3) = 10 AND FALSE OR CONCAT('1', '2', '3') = 124")
);
}
}

View File

@ -0,0 +1,92 @@
package io.cloudevents.bench.sql;
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import io.cloudevents.sql.Expression;
import io.cloudevents.sql.Parser;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import static io.cloudevents.core.test.Data.V1_WITH_JSON_DATA_WITH_EXT;
public class RunBenchmark {
@State(Scope.Thread)
public static class TestCaseSmallExpression {
public CloudEvent event = CloudEventBuilder
.v1(V1_WITH_JSON_DATA_WITH_EXT)
.withExtension("a", "10")
.withExtension("b", 3)
.withExtension("c", "-3")
.build();
public Expression expression = Parser
.parseDefault("(a + b + c) = 10 AND TRUE OR CONCAT('1', '2', id) = '123'");
}
@State(Scope.Thread)
public static class TestCaseSmallTrueConstantExpression {
public CloudEvent event = CloudEventBuilder
.v1(V1_WITH_JSON_DATA_WITH_EXT)
.build();
public Expression expression = Parser
.parseDefault("(1 + 2 + 3) = 10 AND TRUE OR CONCAT('1', '2', '3') = 123");
}
@State(Scope.Thread)
public static class TestCaseSmallFalseConstantExpression {
public CloudEvent event = CloudEventBuilder
.v1(V1_WITH_JSON_DATA_WITH_EXT)
.build();
public Expression expression = Parser
.parseDefault("(1 + 2 + 3) = 10 AND FALSE OR CONCAT('1', '2', '3') = 124");
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
public void testEvaluateSmallExpression(TestCaseSmallExpression testCase, Blackhole bh) {
bh.consume(
testCase.expression.evaluate(testCase.event)
);
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
public void testTryEvaluateSmallExpression(TestCaseSmallExpression testCase, Blackhole bh) {
bh.consume(
testCase.expression.tryEvaluate(testCase.event)
);
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
public void testEvaluateSmallTrueConstantExpression(TestCaseSmallTrueConstantExpression testCase, Blackhole bh) {
bh.consume(
testCase.expression.evaluate(testCase.event)
);
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
public void testTryEvaluateSmallTrueConstantExpression(TestCaseSmallTrueConstantExpression testCase, Blackhole bh) {
bh.consume(
testCase.expression.tryEvaluate(testCase.event)
);
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
public void testEvaluateSmallFalseConstantExpression(TestCaseSmallFalseConstantExpression testCase, Blackhole bh) {
bh.consume(
testCase.expression.evaluate(testCase.event)
);
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
public void testTryEvaluateSmallFalseConstantExpression(TestCaseSmallFalseConstantExpression testCase, Blackhole bh) {
bh.consume(
testCase.expression.tryEvaluate(testCase.event)
);
}
}

91
bom/pom.xml Normal file
View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
~
-->
<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>
</parent>
<artifactId>cloudevents-bom</artifactId>
<name>CloudEvents - Bill of Materials</name>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-json-jackson</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-protobuf</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-amqp-proton</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-http-basic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-http-vertx</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-http-restful-ws</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-kafka</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-spring</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-sql</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@ -1,67 +1,5 @@
# CloudEvents Core
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-core.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-core)
Javadoc: [![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-core.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-core)
The base classes, interfaces and low-level APIs to use CloudEvents.
## How to Use
For Maven based projects, use the following dependency:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-api</artifactId>
<version>2.0.0.RC1</version>
</dependency>
```
### Create an Event
```java
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import java.net.URI;
final CloudEvent event = CloudEventBuilder.v1()
.withId("000")
.withType("example.demo")
.withSource(URI.create("http://example.com"))
.withData("application/json", "{}".getBytes())
.build();
```
### Materialize an Extension
CloudEvent extensions can be materialized in their respective POJOs:
```java
import io.cloudevents.core.extensions.DistributedTracingExtension;
import io.cloudevents.core.extensions.ExtensionsParser;
DistributedTracingExtension dte = ExtensionsParser.getInstance()
.parseExtension(DistributedTracingExtension.class, event);
```
### Using Event Formats
The SDK implements [Event Formats](https://github.com/cloudevents/spec/blob/v1.0/spec.md#event-format) in submodules.
To use them, you just need to add them as dependencies to your project and the SDK,
through the `ServiceLoader` mechanism, will load them into the classpath.
For example, to use the [JSON event format](https://github.com/cloudevents/spec/blob/v1.0/json-format.md) with Jackson,
add `cloudevents-json-jackson` as a dependency and then:
```java
import io.cloudevents.core.format.EventFormatProvider;
import io.cloudevents.jackson.JsonFormat;
EventFormat format = EventFormatProvider
.getInstance()
.resolveFormat(JsonFormat.CONTENT_TYPE);
// Serialize event
byte[] serialized = format.serialize(event);
// Deserialize event
CloudEvent event = format.deserialize(bytes);
```
Documentation: https://cloudevents.github.io/sdk-java/core

View File

@ -22,7 +22,7 @@
<parent>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>4.1.0-SNAPSHOT</version>
</parent>
<artifactId>cloudevents-core</artifactId>

View File

@ -30,7 +30,7 @@ import io.cloudevents.rw.CloudEventRWException;
import io.cloudevents.rw.CloudEventReader;
/**
* This class contains a set of utility methods to deal with conversions of io.cloudevents related interfaces
* This class contains a set of utility methods to deal with conversions of {@link io.cloudevents} related interfaces
*/
public final class CloudEventUtils {
@ -38,8 +38,7 @@ public final class CloudEventUtils {
/**
* Convert a {@link CloudEvent} to a {@link CloudEventReader}. This method provides a default implementation
* for CloudEvent that doesn't implement {@link CloudEventReader}
* for CloudEvent that doesn't implement CloudEventVisitable.
* for CloudEvent that doesn't implement {@link CloudEventReader}.
* <p>
* It's safe to use the returned {@link CloudEventReader} multiple times.
*
@ -94,6 +93,11 @@ public final class CloudEventUtils {
/**
* Get the data contained in {@code event} and map it using the provided mapper.
*
* @param event the event eventually containing the data
* @param mapper the mapper to use to map the data
* @param <R> the returned {@link CloudEventData} implementation from the provided mapper
* @return the data contained in {@code event} and mapped with {@code mapper}, if any, otherwise null
*/
@Nullable
public static <R extends CloudEventData> R mapData(CloudEvent event, CloudEventDataMapper<R> mapper) {

View File

@ -17,18 +17,13 @@
package io.cloudevents.core.builder;
import java.net.URI;
import java.time.OffsetDateTime;
import io.cloudevents.*;
import io.cloudevents.rw.CloudEventWriter;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNullableByDefault;
import io.cloudevents.CloudEvent;
import io.cloudevents.CloudEventContext;
import io.cloudevents.CloudEventData;
import io.cloudevents.Extension;
import io.cloudevents.SpecVersion;
import io.cloudevents.rw.CloudEventWriter;
import java.net.URI;
import java.time.OffsetDateTime;
/**
* Builder interface to build a {@link CloudEvent}.
@ -146,6 +141,28 @@ public interface CloudEventBuilder extends CloudEventWriter<CloudEvent> {
*/
CloudEventBuilder withData(String dataContentType, URI dataSchema, CloudEventData data);
/**
* Remove the {@code datacontenttype}, {@code dataschema} and {@code data} from the event
*
* @return self
*/
CloudEventBuilder withoutData();
/**
* Remove the {@code dataschema} from the event
*
* @return self
*/
CloudEventBuilder withoutDataSchema();
/**
* Remove the {@code datacontenttype} from the event
*
* @return self
*/
CloudEventBuilder withoutDataContentType();
/**
* Set an extension with provided key and string value
*
@ -153,7 +170,6 @@ public interface CloudEventBuilder extends CloudEventWriter<CloudEvent> {
* @param value value of the extension attribute
* @return self
*/
@Override
CloudEventBuilder withExtension(@Nonnull String key, @Nonnull String value);
/**
@ -163,7 +179,6 @@ public interface CloudEventBuilder extends CloudEventWriter<CloudEvent> {
* @param value value of the extension attribute
* @return self
*/
@Override
CloudEventBuilder withExtension(@Nonnull String key, @Nonnull Number value);
/**
@ -173,16 +188,42 @@ public interface CloudEventBuilder extends CloudEventWriter<CloudEvent> {
* @param value value of the extension attribute
* @return self
*/
@Override
CloudEventBuilder withExtension(@Nonnull String key, @Nonnull Boolean value);
/**
* Set an extension with provided key and uri value
*
* @param key key of the extension attribute
* @param value value of the extension attribute
* @return self
*/
CloudEventBuilder withExtension(@Nonnull String key, @Nonnull URI value);
/**
* Set an extension with provided key and boolean value
*
* @param key key of the extension attribute
* @param value value of the extension attribute
* @return self
*/
CloudEventBuilder withExtension(@Nonnull String key, @Nonnull OffsetDateTime value);
/**
* Set an extension with provided key and binary value
*
* @param key key of the extension attribute
* @param value value of the extension attribute
* @return self
*/
CloudEventBuilder withExtension(@Nonnull String key, @Nonnull byte[] value);
/**
* Add to the builder all the extension key/values of the provided extension
*
* @param extension materialized extension to set in the builder
* @return self
*/
CloudEventBuilder withExtension(@Nonnull Extension extension);
CloudEventBuilder withExtension(@Nonnull CloudEventExtension extension);
/**
* Remove from the the builder the provided extension key, if any
@ -198,7 +239,7 @@ public interface CloudEventBuilder extends CloudEventWriter<CloudEvent> {
* @param extension materialized extension to remove from the builder
* @return self
*/
CloudEventBuilder withoutExtension(@Nonnull Extension extension);
CloudEventBuilder withoutExtension(@Nonnull CloudEventExtension extension);
/**
* Build the event
@ -290,9 +331,9 @@ public interface CloudEventBuilder extends CloudEventWriter<CloudEvent> {
static CloudEventBuilder fromContext(@Nonnull CloudEventContext context) {
switch (context.getSpecVersion()) {
case V1:
return new io.cloudevents.core.v1.CloudEventBuilder(context);
return new io.cloudevents.core.v1.CloudEventBuilder(context);
case V03:
return new io.cloudevents.core.v03.CloudEventBuilder(context);
return new io.cloudevents.core.v03.CloudEventBuilder(context);
}
throw new IllegalStateException(
"The provided spec version doesn't exist. Please make sure your io.cloudevents deps versions are aligned."

View File

@ -5,11 +5,15 @@ import io.cloudevents.CloudEventData;
import java.util.Arrays;
import java.util.Objects;
/**
* An implementation of {@link CloudEventData} that wraps a byte array.
*/
public class BytesCloudEventData implements CloudEventData {
private final byte[] value;
/**
* @param value the bytes to wrap
* @deprecated use {@link BytesCloudEventData#wrap(byte[])}
*/
public BytesCloudEventData(byte[] value) {

View File

@ -5,6 +5,11 @@ import io.cloudevents.rw.CloudEventRWException;
import java.util.Objects;
/**
* An implementation of {@link CloudEventData} that wraps any POJO.
*
* @param <T> the type of the wrapped POJO.
*/
public class PojoCloudEventData<T> implements CloudEventData {
/**
@ -15,6 +20,11 @@ public class PojoCloudEventData<T> implements CloudEventData {
*/
@FunctionalInterface
public interface ToBytes<T> {
/**
* @param data the POJO to convert
* @return the serialized byte array.
* @throws Exception when something goes wrong during the conversion.
*/
byte[] convert(T data) throws Exception;
}
@ -36,6 +46,9 @@ public class PojoCloudEventData<T> implements CloudEventData {
this.mapper = mapper;
}
/**
* @return the wrapped POJO
*/
public T getValue() {
return value;
}
@ -67,6 +80,11 @@ public class PojoCloudEventData<T> implements CloudEventData {
/**
* Wrap the provided data in a {@link PojoCloudEventData} serializable by the provided mapper.
*
* @param <T> The type of {@code data}
* @param data the POJO to wrap
* @param mapper converter from {@code data} to bytes, used to implement {@link #toBytes()}
* @return the new {@link PojoCloudEventData}
*/
public static <T> PojoCloudEventData<T> wrap(T data, ToBytes<T> mapper) {
return new PojoCloudEventData<>(data, mapper);

View File

@ -18,8 +18,9 @@
package io.cloudevents.core.extensions;
import io.cloudevents.CloudEventExtensions;
import io.cloudevents.Extension;
import io.cloudevents.CloudEventExtension;
import io.cloudevents.core.extensions.impl.ExtensionUtils;
import java.net.URI;
import java.util.Collections;
import java.util.HashSet;
@ -27,19 +28,30 @@ import java.util.Set;
/**
* This extension supports the "Claim Check Pattern". It allows to specify a reference to a location where the event payload is stored.
*
* @see <a href=https://github.com/cloudevents/spec/blob/v1.0/extensions/dataref.md>https://github.com/cloudevents/spec/blob/v1.0/extensions/dataref.md</a>
*/
public final class DatarefExtension implements Extension {
public final class DatarefExtension implements CloudEventExtension {
/**
* The key of the {@code dataref} extension
*/
public static final String DATAREF = "dataref";
private static final Set<String> KEY_SET = Collections.unmodifiableSet(new HashSet<>(Collections.singletonList(DATAREF)));
private URI dataref;
/**
* @return the {@code dataref} contained in this extension.
*/
public URI getDataref() {
return dataref;
}
/**
* @param dataref the uri to set as {@code dataref}.
*/
public void setDataref(URI dataref) {
this.dataref = dataref;
}
@ -57,7 +69,7 @@ public final class DatarefExtension implements Extension {
if (DATAREF.equals(key)) {
return this.dataref.toString();
}
throw ExtensionUtils.generateInvalidKeyException(this.getClass().getSimpleName(), key);
throw ExtensionUtils.generateInvalidKeyException(this.getClass(), key);
}
@Override

View File

@ -18,40 +18,57 @@
package io.cloudevents.core.extensions;
import io.cloudevents.CloudEventExtensions;
import io.cloudevents.Extension;
import io.cloudevents.CloudEventExtension;
import io.cloudevents.core.extensions.impl.ExtensionUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
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 Extension {
public final class DistributedTracingExtension implements CloudEventExtension {
/**
* The key of the {@code traceparent} extension
*/
public static final String TRACEPARENT = "traceparent";
/**
* The key of the {@code tracestate} extension
*/
public static final String TRACESTATE = "tracestate";
private static final Set<String> KEY_SET = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(TRACEPARENT, TRACESTATE)));
private String traceparent;
private String tracestate;
/**
* @return the {@code traceparent} contained in this extension.
*/
public String getTraceparent() {
return traceparent;
}
/**
* @param traceparent the string to set as {@code traceparent}.
*/
public void setTraceparent(String traceparent) {
this.traceparent = traceparent;
}
/**
* @return the {@code tracestate} contained in this extension.
*/
public String getTracestate() {
return tracestate;
}
/**
* @param tracestate the string to set as {@code tracestate}.
*/
public void setTracestate(String tracestate) {
this.tracestate = tracestate;
}
@ -76,7 +93,7 @@ public final class DistributedTracingExtension implements Extension {
case TRACESTATE:
return this.tracestate;
}
throw ExtensionUtils.generateInvalidKeyException(this.getClass().getSimpleName(), key);
throw ExtensionUtils.generateInvalidKeyException(this.getClass(), key);
}
@Override
@ -93,35 +110,16 @@ public final class DistributedTracingExtension implements Extension {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((traceparent == null) ? 0
: traceparent.hashCode());
result = prime * result + ((tracestate == null) ? 0
: tracestate.hashCode());
return result;
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DistributedTracingExtension that = (DistributedTracingExtension) o;
return Objects.equals(getTraceparent(), that.getTraceparent()) &&
Objects.equals(getTracestate(), that.getTracestate());
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DistributedTracingExtension other = (DistributedTracingExtension) obj;
if (traceparent == null) {
if (other.traceparent != null)
return false;
} else if (!traceparent.equals(other.traceparent))
return false;
if (tracestate == null) {
if (other.tracestate != null)
return false;
} else if (!tracestate.equals(other.tracestate))
return false;
return true;
public int hashCode() {
return Objects.hash(getTraceparent(), getTracestate());
}
}

View File

@ -17,13 +17,23 @@
package io.cloudevents.core.extensions.impl;
import io.cloudevents.CloudEventExtension;
/**
* Collection of utilities to deal with materialized extensions
*/
public final class ExtensionUtils {
private ExtensionUtils() {
}
public static IllegalArgumentException generateInvalidKeyException(String extensionName, String key) {
return new IllegalArgumentException(extensionName + " doesn't expect the attribute key \"" + key + "\"");
/**
* @param clazz the {@link CloudEventExtension} class
* @param key the invalid key
* @return an {@link IllegalArgumentException} when trying to access a key of the extension not existing.
*/
public static IllegalArgumentException generateInvalidKeyException(Class<? extends CloudEventExtension> clazz, String key) {
return new IllegalArgumentException(clazz.getName() + " doesn't expect the attribute key \"" + key + "\"");
}
}

View File

@ -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(); }
}

View File

@ -21,7 +21,10 @@ package io.cloudevents.core.format;
* Exception representing a deserialization error while using an {@link EventFormat}.
*/
public class EventDeserializationException extends RuntimeException {
public EventDeserializationException(Throwable e) {
super(e);
/**
* @param cause the cause of the exception
*/
public EventDeserializationException(Throwable cause) {
super(cause);
}
}

View File

@ -48,18 +48,21 @@ public interface EventFormat {
byte[] serialize(CloudEvent event) throws EventSerializationException;
/**
* Deserialize a byte array to a {@link CloudEvent}.
* Like {@link #deserialize(byte[], CloudEventDataMapper)}, but with the identity {@link CloudEventDataMapper}.
*
* @param bytes the serialized event.
* @return the deserialized event.
* @throws EventDeserializationException if something goes wrong during deserialization.
* @see #deserialize(byte[], CloudEventDataMapper)
*/
default CloudEvent deserialize(byte[] bytes) throws EventDeserializationException {
return this.deserialize(bytes, null);
return this.deserialize(bytes, CloudEventDataMapper.identity());
}
/**
* Like {@link EventFormat#deserialize(byte[])}, but allows a mapper that maps the parsed {@link io.cloudevents.CloudEventData} to another one.
* Deserialize a byte array to a {@link CloudEvent}.
*
* @param bytes the serialized event.
* @param mapper the mapper to use to map the data.
* @return the deserialized event.
* @throws EventDeserializationException if something goes wrong during deserialization.
*/
CloudEvent deserialize(byte[] bytes, CloudEventDataMapper<? extends CloudEventData> mapper) throws EventDeserializationException;

View File

@ -21,7 +21,10 @@ package io.cloudevents.core.format;
* Exception representing a serialization error while using an {@link EventFormat}.
*/
public class EventSerializationException extends RuntimeException {
public EventSerializationException(Throwable e) {
super(e);
/**
* @param cause the cause of the exception
*/
public EventSerializationException(Throwable cause) {
super(cause);
}
}

View File

@ -21,6 +21,8 @@ import io.cloudevents.CloudEvent;
import io.cloudevents.CloudEventData;
import io.cloudevents.rw.*;
import java.net.URI;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@ -52,26 +54,31 @@ public abstract class BaseCloudEvent implements CloudEvent, CloudEventReader, Cl
@Override
public <T extends CloudEventWriter<V>, V> V read(CloudEventWriterFactory<T, V> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException, IllegalStateException {
CloudEventWriter<V> visitor = writerFactory.create(this.getSpecVersion());
this.readAttributes(visitor);
this.readExtensions(visitor);
CloudEventWriter<V> writer = writerFactory.create(this.getSpecVersion());
this.readContext(writer);
if (this.data != null) {
return visitor.end(mapper.map(this.data));
return writer.end(mapper.map(this.data));
}
return visitor.end();
return writer.end();
}
public void readExtensions(CloudEventExtensionsWriter writer) throws CloudEventRWException {
protected void readExtensions(CloudEventContextWriter writer) throws CloudEventRWException {
// TODO to be improved
for (Map.Entry<String, Object> entry : this.extensions.entrySet()) {
if (entry.getValue() instanceof String) {
writer.withExtension(entry.getKey(), (String) entry.getValue());
writer.withContextAttribute(entry.getKey(), (String) entry.getValue());
} else if (entry.getValue() instanceof Number) {
writer.withExtension(entry.getKey(), (Number) entry.getValue());
writer.withContextAttribute(entry.getKey(), (Number) entry.getValue());
} else if (entry.getValue() instanceof Boolean) {
writer.withExtension(entry.getKey(), (Boolean) entry.getValue());
writer.withContextAttribute(entry.getKey(), (Boolean) entry.getValue());
} else if (entry.getValue() instanceof URI) {
writer.withContextAttribute(entry.getKey(), (URI) entry.getValue());
} else if (entry.getValue() instanceof OffsetDateTime) {
writer.withContextAttribute(entry.getKey(), (OffsetDateTime) entry.getValue());
} else if (entry.getValue() instanceof byte[]) {
writer.withContextAttribute(entry.getKey(), (byte[]) entry.getValue());
} else {
// This should never happen because we build that map only through our builders
throw new IllegalStateException("Illegal value inside extensions map: " + entry);

View File

@ -17,20 +17,21 @@
package io.cloudevents.core.impl;
import io.cloudevents.CloudEvent;
import io.cloudevents.CloudEventContext;
import io.cloudevents.CloudEventData;
import io.cloudevents.CloudEventExtension;
import io.cloudevents.core.builder.CloudEventBuilder;
import io.cloudevents.core.data.BytesCloudEventData;
import io.cloudevents.rw.CloudEventRWException;
import javax.annotation.Nonnull;
import java.net.URI;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import io.cloudevents.CloudEvent;
import io.cloudevents.CloudEventContext;
import io.cloudevents.CloudEventData;
import io.cloudevents.Extension;
import io.cloudevents.core.builder.CloudEventBuilder;
import io.cloudevents.core.data.BytesCloudEventData;
import io.cloudevents.rw.CloudEventRWException;
import static io.cloudevents.core.v03.CloudEventV03.SPECVERSION;
public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<SELF, T>, T extends CloudEvent> implements CloudEventBuilder {
@ -97,6 +98,24 @@ public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<S
return this.self;
}
@Override
public CloudEventBuilder withoutData() {
this.data = null;
return this.self;
}
@Override
public CloudEventBuilder withoutDataSchema() {
withDataSchema(null);
return this.self;
}
@Override
public CloudEventBuilder withoutDataContentType() {
withDataContentType(null);
return this.self;
}
public SELF withExtension(@Nonnull String key, @Nonnull String value) {
if (!isValidExtensionName(key)) {
throw CloudEventRWException.newInvalidExtensionName(key);
@ -105,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);
@ -113,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);
@ -121,6 +151,33 @@ public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<S
return self;
}
@Override
public SELF withExtension(@Nonnull String key, @Nonnull URI value) {
if (!isValidExtensionName(key)) {
throw CloudEventRWException.newInvalidExtensionName(key);
}
this.extensions.put(key, value);
return self;
}
@Override
public SELF withExtension(@Nonnull String key, @Nonnull OffsetDateTime value) {
if (!isValidExtensionName(key)) {
throw CloudEventRWException.newInvalidExtensionName(key);
}
this.extensions.put(key, value);
return self;
}
@Override
public CloudEventBuilder withExtension(@Nonnull String key, @Nonnull byte[] value) {
if (!isValidExtensionName(key)) {
throw CloudEventRWException.newInvalidExtensionName(key);
}
this.extensions.put(key, value);
return self;
}
@Override
public SELF withoutExtension(@Nonnull String key) {
this.extensions.remove(key);
@ -128,12 +185,12 @@ public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<S
}
@Override
public SELF withoutExtension(@Nonnull Extension extension) {
public SELF withoutExtension(@Nonnull CloudEventExtension extension) {
extension.getKeys().forEach(this::withoutExtension);
return self;
}
public SELF withExtension(@Nonnull Extension extension) {
public SELF withExtension(@Nonnull CloudEventExtension extension) {
for (String key : extension.getKeys()) {
Object value = extension.getValue(key);
if (value != null) {
@ -161,20 +218,23 @@ public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<S
protected static IllegalStateException createMissingAttributeException(String attributeName) {
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
* See <a href="https://github.com/cloudevents/spec/blob/master/spec.md#attribute-naming-convention">attribute-naming-convention</a>
* 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/main/cloudevents/spec.md#naming-conventions">attribute-naming-conventions</a>
*/
private static boolean isValidExtensionName(String name) {
if(name.length() > 20){
return false;
}
char[] chars = name.toCharArray();
for (char c: chars)
if (!isValidChar(c)) {
return false;
for (int i = 0; i < name.length(); i++) {
if (!isValidChar(name.charAt(i))) {
return false;
}
}
return true;
}
@ -182,4 +242,10 @@ public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<S
private static boolean isValidChar(char c) {
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
}
protected void requireValidAttributeWrite(String name) {
if (name.equals(SPECVERSION)) {
throw new IllegalArgumentException("You should not set the specversion attribute through withContextAttribute methods");
}
}
}

View File

@ -18,9 +18,12 @@
package io.cloudevents.core.impl;
import io.cloudevents.CloudEventContext;
import io.cloudevents.rw.CloudEventAttributesWriter;
import io.cloudevents.rw.CloudEventContextReader;
import io.cloudevents.rw.CloudEventExtensionsWriter;
import io.cloudevents.rw.CloudEventContextWriter;
import io.cloudevents.rw.CloudEventRWException;
import java.net.URI;
import java.time.OffsetDateTime;
public class CloudEventContextReaderAdapter implements CloudEventContextReader {
@ -30,41 +33,47 @@ public class CloudEventContextReaderAdapter implements CloudEventContextReader {
this.event = event;
}
@Override
public void readAttributes(CloudEventAttributesWriter writer) throws RuntimeException {
writer.withAttribute("id", event.getId());
writer.withAttribute("source", event.getSource());
writer.withAttribute("type", event.getType());
public void readAttributes(CloudEventContextWriter writer) throws RuntimeException {
writer.withContextAttribute("id", event.getId());
writer.withContextAttribute("source", event.getSource());
writer.withContextAttribute("type", event.getType());
if (event.getDataContentType() != null) {
writer.withAttribute("datacontenttype", event.getDataContentType());
writer.withContextAttribute("datacontenttype", event.getDataContentType());
}
if (event.getDataSchema() != null) {
writer.withAttribute("dataschema", event.getDataSchema());
writer.withContextAttribute("dataschema", event.getDataSchema());
}
if (event.getSubject() != null) {
writer.withAttribute("subject", event.getSubject());
writer.withContextAttribute("subject", event.getSubject());
}
if (event.getTime() != null) {
writer.withAttribute("time", event.getTime());
writer.withContextAttribute("time", event.getTime());
}
}
@Override
public void readExtensions(CloudEventExtensionsWriter writer) throws RuntimeException {
public void readExtensions(CloudEventContextWriter writer) throws RuntimeException {
for (String key : event.getExtensionNames()) {
Object value = event.getExtension(key);
if (value instanceof String) {
writer.withExtension(key, (String) value);
writer.withContextAttribute(key, (String) value);
} else if (value instanceof Number) {
writer.withExtension(key, (Number) value);
writer.withContextAttribute(key, (Number) value);
} else if (value instanceof Boolean) {
writer.withExtension(key, (Boolean) value);
writer.withContextAttribute(key, (Boolean) value);
} else if (value instanceof URI) {
writer.withContextAttribute(key, (URI) value);
} else if (value instanceof OffsetDateTime) {
writer.withContextAttribute(key, (OffsetDateTime) value);
} else {
// This should never happen because we build that map only through our builders
throw new IllegalStateException("Illegal value inside extensions map: " + key + " " + value);
}
}
;
}
@Override
public void readContext(CloudEventContextWriter writer) throws CloudEventRWException {
this.readAttributes(writer);
this.readExtensions(writer);
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.impl;
import javax.annotation.Nonnull;
final public class StringUtils {
private StringUtils() {
// Prevent construction.
}
public static boolean startsWithIgnoreCase(@Nonnull final String s, @Nonnull final String prefix) {
return s.regionMatches(true /* ignoreCase */, 0, prefix, 0, prefix.length());
}
}

View File

@ -21,6 +21,12 @@ package io.cloudevents.core.message;
* One of the possible encodings of a <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#message">CloudEvent message</a>
*/
public enum Encoding {
/**
* Structured mode
*/
STRUCTURED,
/**
* Binary mode
*/
BINARY
}

View File

@ -19,41 +19,50 @@ package io.cloudevents.core.message;
import io.cloudevents.CloudEvent;
import io.cloudevents.CloudEventData;
import io.cloudevents.SpecVersion;
import io.cloudevents.core.CloudEventUtils;
import io.cloudevents.rw.*;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Represents a <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#message">CloudEvent message</a>.
* Represents a <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#message">CloudEvent message</a> reader.
* <p>
* This class expands the {@link CloudEventReader} to define reading both binary and structured messages.
*/
@ParametersAreNonnullByDefault
public interface MessageReader extends StructuredMessageReader, CloudEventReader {
/**
* Visit the message as binary encoded event using the provided visitor factory.
* Like {@link #read(CloudEventWriterFactory, CloudEventDataMapper)}, but with the identity {@link CloudEventDataMapper}.
*
* @param writerFactory a factory that generates a visitor starting from the SpecVersion of the event
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message is not in binary encoding.
* @see #read(CloudEventWriterFactory, CloudEventDataMapper)
*/
default <V extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<V, R> writerFactory) throws CloudEventRWException, IllegalStateException {
default <W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory) throws CloudEventRWException, IllegalStateException {
return read(writerFactory, CloudEventDataMapper.identity());
}
/**
* Like {@link MessageReader#read(CloudEventWriterFactory)}, but providing a mapper for {@link io.cloudevents.CloudEventData} to be invoked when the data field is available.
* Read the message as binary encoded message using the provided writer factory.
*
* @param <W> the {@link CloudEventWriter} type
* @param <R> the return type of the {@link CloudEventWriter}
* @param writerFactory a factory that generates a reader starting from the {@link SpecVersion} of the event
* @param mapper the mapper to use to map the data, if any.
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message is not in binary encoding.
*/
<V extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<V, R> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException, IllegalStateException;
<W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException, IllegalStateException;
/**
* Visit the message as structured encoded event using the provided visitor
* Read the message as structured encoded message using the provided writer
*
* @param visitor Structured Message visitor
* @param <R> the return type of the {@link StructuredMessageWriter}
* @param writer Structured Message writer
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message is not in structured encoding.
* @throws IllegalStateException if the message is not in structured encoding.
*/
<T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException;
<R> R read(StructuredMessageWriter<R> writer) throws CloudEventRWException, IllegalStateException;
/**
* @return The message encoding
@ -64,38 +73,41 @@ public interface MessageReader extends StructuredMessageReader, CloudEventReader
* Read the content of this object using a {@link MessageWriter}. This method allows to transcode an event from one transport to another without
* converting it to {@link CloudEvent}. The resulting encoding will be the same as the original encoding.
*
* @param visitor the MessageVisitor accepting this Message
* @return The return value of the MessageVisitor
* @param <BW> the {@link CloudEventWriter} type
* @param <R> the return type of both {@link CloudEventWriter} and {@link StructuredMessageWriter}
* @param writer the {@link MessageWriter} accepting this Message
* @return The return value of the {@link MessageWriter}
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message has an unknown encoding.
*/
default <BV extends CloudEventWriter<R>, R> R read(MessageWriter<BV, R> visitor) throws CloudEventRWException, IllegalStateException {
default <BW extends CloudEventWriter<R>, R> R read(MessageWriter<BW, R> writer) throws CloudEventRWException, IllegalStateException {
switch (getEncoding()) {
case BINARY:
return this.read((CloudEventWriterFactory<BV, R>) visitor);
return this.read((CloudEventWriterFactory<BW, R>) writer);
case STRUCTURED:
return this.read((StructuredMessageWriter<R>) visitor);
return this.read((StructuredMessageWriter<R>) writer);
default:
throw new IllegalStateException("Unknown encoding");
throw new IllegalStateException(
"The provided Encoding doesn't exist. Please make sure your io.cloudevents deps versions are aligned."
);
}
}
/**
* Translate this message into a {@link CloudEvent} representation.
* Like {@link #toEvent(CloudEventDataMapper)}, but with the identity {@link CloudEventDataMapper}.
*
* @return A {@link CloudEvent} with the contents of this message.
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message has an unknown encoding.
* @see #toEvent(CloudEventDataMapper)
*/
default CloudEvent toEvent() throws CloudEventRWException, IllegalStateException {
return toEvent(CloudEventDataMapper.identity());
}
/**
* Translate this message into a {@link CloudEvent} representation.
* Translate this message into a {@link CloudEvent} representation, mapping the data with the provided {@code mapper}.
*
* @param mapper the mapper to use to map the data, if any.
* @return A {@link CloudEvent} with the contents of this message.
* @throws CloudEventRWException if something went wrong during the visit.
* @throws CloudEventRWException if something went wrong during the read.
* @throws IllegalStateException if the message has an unknown encoding.
*/
default CloudEvent toEvent(CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException, IllegalStateException {
@ -105,7 +117,9 @@ public interface MessageReader extends StructuredMessageReader, CloudEventReader
case STRUCTURED:
return this.read((format, value) -> format.deserialize(value, mapper));
default:
throw new IllegalStateException("Unknown encoding");
throw new IllegalStateException(
"The provided Encoding doesn't exist. Please make sure your io.cloudevents deps versions are aligned."
);
}
}

View File

@ -34,11 +34,15 @@ import javax.annotation.ParametersAreNonnullByDefault;
public interface StructuredMessageReader {
/**
* @param visitor
* Read self using the provided writer.
*
* @param <R> the return type of the {@link StructuredMessageWriter}
* @param writer the writer to use to write out the message
* @return the return value returned by {@link StructuredMessageWriter#setEvent(EventFormat, byte[])}
* @throws CloudEventRWException If something went wrong when
* @throws IllegalStateException If the message is not a valid structured message
*/
<T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException;
<R> R read(StructuredMessageWriter<R> writer) throws CloudEventRWException, IllegalStateException;
default CloudEvent toEvent() throws CloudEventRWException, IllegalStateException {
return this.read(EventFormat::deserialize);
@ -49,9 +53,9 @@ public interface StructuredMessageReader {
}
/**
* Create a generic structured message from a {@link CloudEvent}
* Create a generic structured message from a {@link CloudEvent}.
*
* @param event
* @param event the event to convert to {@link StructuredMessageReader}
* @param contentType content type to use to resolve the {@link EventFormat}
* @return null if format was not found, otherwise returns the built message
*/
@ -60,10 +64,10 @@ public interface StructuredMessageReader {
}
/**
* Create a generic structured message from a {@link CloudEvent}
* Create a generic structured message from a {@link CloudEvent}.
*
* @param event
* @param format
* @param event the event to convert to {@link StructuredMessageReader}
* @param format the format to use to perform the conversion
* @return null if format was not found, otherwise returns the built message
*/
static StructuredMessageReader from(CloudEvent event, EventFormat format) {

View File

@ -25,15 +25,15 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface to write the {@link MessageReader} content (CloudEvents attributes, extensions and payload) to a new representation structured representation.
*
* @param <T> return value at the end of the write process.
* @param <R> return value at the end of the write process.
*/
@ParametersAreNonnullByDefault
@FunctionalInterface
public interface StructuredMessageWriter<T> {
public interface StructuredMessageWriter<R> {
/**
* Write an event using the provided {@link EventFormat}.
*/
T setEvent(EventFormat format, byte[] value) throws CloudEventRWException;
R setEvent(EventFormat format, byte[] value) throws CloudEventRWException;
}

View File

@ -22,6 +22,9 @@ import io.cloudevents.core.message.MessageReader;
import io.cloudevents.core.message.StructuredMessageWriter;
import io.cloudevents.rw.CloudEventRWException;
/**
* Base {@link MessageReader} implementation for a binary message
*/
public abstract class BaseBinaryMessageReader implements MessageReader {
@Override
@ -30,7 +33,7 @@ public abstract class BaseBinaryMessageReader implements MessageReader {
}
@Override
public <T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException {
public <T> T read(StructuredMessageWriter<T> writer) throws CloudEventRWException, IllegalStateException {
throw MessageUtils.generateWrongEncoding(Encoding.STRUCTURED, Encoding.BINARY);
}
}

View File

@ -56,18 +56,17 @@ public abstract class BaseGenericBinaryMessageReaderImpl<HK, HV> extends BaseBin
// This implementation avoids to use visitAttributes and visitExtensions
// in order to complete the visit in one loop
this.forEachHeader((key, value) -> {
if (value == null) {
return;
}
if (isContentTypeHeader(key)) {
visitor.withAttribute(CloudEventV1.DATACONTENTTYPE, toCloudEventsValue(value));
visitor.withContextAttribute(CloudEventV1.DATACONTENTTYPE, toCloudEventsValue(value));
} else if (isCloudEventsHeader(key)) {
String name = toCloudEventsKey(key);
if (name.equals(CloudEventV1.SPECVERSION)) {
return;
}
if (this.version.getAllAttributes().contains(name)) {
visitor.withAttribute(name, toCloudEventsValue(value));
} else {
visitor.withExtension(name, toCloudEventsValue(value));
}
visitor.withContextAttribute(name, toCloudEventsValue(value));
}
});
@ -79,14 +78,35 @@ public abstract class BaseGenericBinaryMessageReaderImpl<HK, HV> extends BaseBin
return visitor.end();
}
/**
* @param key header key
* @return true if this header is the content type header, false otherwise
*/
protected abstract boolean isContentTypeHeader(HK key);
/**
* @param key header key
* @return true if this header is a CloudEvents header, false otherwise
*/
protected abstract boolean isCloudEventsHeader(HK key);
/**
* @param key header key
* @return the key converted to a CloudEvents context attribute/extension name
*/
protected abstract String toCloudEventsKey(HK key);
/**
* Iterate over all the headers in the headers map.
*
* @param fn header consumer
*/
protected abstract void forEachHeader(BiConsumer<HK, HV> fn);
/**
* @param value header key
* @return the value converted to a valid CloudEvents attribute value as {@link String}.
*/
protected abstract String toCloudEventsValue(HV value);
}

View File

@ -24,6 +24,9 @@ import io.cloudevents.rw.CloudEventDataMapper;
import io.cloudevents.rw.CloudEventWriter;
import io.cloudevents.rw.CloudEventWriterFactory;
/**
* Base {@link MessageReader} implementation for a structured message
*/
public abstract class BaseStructuredMessageReader implements MessageReader {
@Override

View File

@ -24,6 +24,9 @@ import io.cloudevents.core.provider.EventFormatProvider;
import io.cloudevents.lang.Nullable;
import io.cloudevents.rw.CloudEventRWException;
/**
* Generic implementation of a structured message.
*/
public class GenericStructuredMessageReader extends BaseStructuredMessageReader {
private final EventFormat format;
@ -35,8 +38,8 @@ public class GenericStructuredMessageReader extends BaseStructuredMessageReader
}
@Override
public <T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException {
return visitor.setEvent(format, payload);
public <T> T read(StructuredMessageWriter<T> writer) throws CloudEventRWException, IllegalStateException {
return writer.setEvent(format, payload);
}
/**

View File

@ -22,6 +22,7 @@ import io.cloudevents.core.format.EventFormat;
import io.cloudevents.core.message.Encoding;
import io.cloudevents.core.message.MessageReader;
import io.cloudevents.core.provider.EventFormatProvider;
import io.cloudevents.rw.CloudEventRWException;
import java.util.Map;
import java.util.function.Function;
@ -31,25 +32,43 @@ import java.util.stream.Stream;
import static io.cloudevents.rw.CloudEventRWException.newUnknownEncodingException;
/**
* Collection of utilities useful to implement {@link MessageReader} and {@link io.cloudevents.core.message.MessageWriter} related code.
*/
public class MessageUtils {
/**
* Common flow to parse an incoming message that could be structured or binary.
*
* @param contentTypeHeaderReader supplier that returns the content type header, if any
* @param structuredMessageFactory factory to create the structured {@link MessageReader} from the provided {@link EventFormat}
* @param specVersionHeaderReader supplier that returns the spec version header, if any
* @param binaryMessageFactory factory to create the binary {@link MessageReader} from the provided {@link SpecVersion}
* @return the instantiated {@link MessageReader}
* @throws CloudEventRWException if something goes wrong while resolving the {@link SpecVersion} or if the message has unknown encoding
*/
public static MessageReader parseStructuredOrBinaryMessage(
Supplier<String> contentTypeHeaderReader,
Function<EventFormat, MessageReader> structuredMessageFactory,
Supplier<String> specVersionHeaderReader,
Function<SpecVersion, MessageReader> binaryMessageFactory
) {
) throws CloudEventRWException {
// Let's try structured mode
String ct = contentTypeHeaderReader.get();
if (ct != null) {
EventFormat format = EventFormatProvider.getInstance().resolveFormat(ct);
if (format != null) {
return structuredMessageFactory.apply(format);
} else {
/**
* The format wasn't one we support, but if it's part of the
* CloudEvent family it indicates it's a structured
* representation that we can't interpret.
*/
if (ct.startsWith("application/cloudevents")) {
throw newUnknownEncodingException();
}
}
}
// Let's try binary mode
@ -77,6 +96,11 @@ public class MessageUtils {
.collect(Collectors.toMap(Function.identity(), headerNameMapping));
}
/**
* @param expected the expected encoding
* @param actual the actual encoding
* @return a new instance of {@link IllegalStateException}.
*/
public static IllegalStateException generateWrongEncoding(Encoding expected, Encoding actual) {
return new IllegalStateException("Cannot visit message as " + expected + " because the actual encoding is " + actual);
}

View File

@ -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);
}
}
}

View File

@ -17,70 +17,96 @@
package io.cloudevents.core.provider;
import java.util.HashMap;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;
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;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.HashMap;
import java.util.ServiceLoader;
import java.util.stream.StreamSupport;
/**
* Singleton holding the discovered {@link EventFormat} implementations through {@link ServiceLoader}.
* Singleton holding the discovered {@link EventFormat} implementations through
* {@link ServiceLoader}.
* <p>
* You can resolve an event format using {@code EventFormatProvider.getInstance().resolveFormat(contentType)}.
* You can resolve an event format using
* {@code EventFormatProvider.getInstance().resolveFormat(contentType)}.
* <p>
* You can programmatically add a new {@link EventFormat} implementation using {@link #registerFormat(EventFormat)}.
* You can programmatically add a new {@link EventFormat} implementation using
* {@link #registerFormat(EventFormat)}.
*/
@ParametersAreNonnullByDefault
public final class EventFormatProvider {
private static class SingletonContainer {
private final static EventFormatProvider INSTANCE = new EventFormatProvider();
}
private static class SingletonContainer {
private final static EventFormatProvider INSTANCE = new EventFormatProvider();
}
/**
* @return instance of {@link EventFormatProvider}
*/
public static EventFormatProvider getInstance() {
return EventFormatProvider.SingletonContainer.INSTANCE;
}
/**
* @return instance of {@link EventFormatProvider}
*/
public static EventFormatProvider getInstance() {
return EventFormatProvider.SingletonContainer.INSTANCE;
}
private final HashMap<String, EventFormat> formats;
private final HashMap<String, EventFormat> formats;
private EventFormatProvider() {
this.formats = new HashMap<>();
private EventFormatProvider() {
this.formats = new HashMap<>();
StreamSupport.stream(
ServiceLoader.load(EventFormat.class).spliterator(),
false
).forEach(this::registerFormat);
}
StreamSupport.stream(ServiceLoader.load(EventFormat.class).spliterator(), false)
.forEach(this::registerFormat);
}
/**
* Register a new {@link EventFormat} programmatically.
*
* @param format the new format to register
*/
public void registerFormat(EventFormat format) {
for (String k : format.deserializableContentTypes()) {
this.formats.put(k, format);
}
}
/**
* Register a new {@link EventFormat} programmatically.
*
* @param format the new format to register
*/
public void registerFormat(EventFormat format) {
for (String k : format.deserializableContentTypes()) {
this.formats.put(k, format);
}
}
/**
* 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(String contentType) {
int i = contentType.indexOf(';');
if (i != -1) {
contentType = contentType.substring(0, i);
}
return this.formats.get(contentType);
}
/**
* Enumerate the supported content types.
*
* @return an alphabetically sorted list of content types
*/
public Set<String> getContentTypes() {
Set<String> types = new TreeSet<>();
types.addAll(this.formats.keySet());
return types;
}
/**
* 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(String contentType) {
int i = contentType.indexOf(';');
if (i != -1) {
contentType = contentType.substring(0, i);
}
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());
}
}

View File

@ -18,7 +18,7 @@
package io.cloudevents.core.provider;
import io.cloudevents.CloudEventExtensions;
import io.cloudevents.Extension;
import io.cloudevents.CloudEventExtension;
import io.cloudevents.core.extensions.DatarefExtension;
import io.cloudevents.core.extensions.DistributedTracingExtension;
import io.cloudevents.lang.Nullable;
@ -30,7 +30,7 @@ import java.util.function.Supplier;
/**
* Singleton to materialize CloudEvent extensions as POJOs.
* <p>
* You can materialize an {@link Extension} POJO with {@code ExtensionProvider.getInstance().parseExtension(DistributedTracingExtension.class, event)}.
* You can materialize an {@link CloudEventExtension} POJO with {@code ExtensionProvider.getInstance().parseExtension(DistributedTracingExtension.class, event)}.
*/
@ParametersAreNonnullByDefault
public final class ExtensionProvider {
@ -39,6 +39,9 @@ public final class ExtensionProvider {
private static final ExtensionProvider INSTANCE = new ExtensionProvider();
}
/**
* @return instance of {@link ExtensionProvider}
*/
public static ExtensionProvider getInstance() {
return SingletonContainer.INSTANCE;
}
@ -55,27 +58,28 @@ public final class ExtensionProvider {
/**
* Register a new extension type.
*
* @param extensionClass the class implementing {@link Extension}
* @param factory the empty arguments factory
* @param <T> the type of the extension
* @param <T> the type of the extension
* @param extensionClass the class implementing {@link CloudEventExtension}
* @param factory the empty arguments factory
*/
public <T extends Extension> void registerExtension(Class<T> extensionClass, Supplier<T> factory) {
public <T extends CloudEventExtension> void registerExtension(Class<T> extensionClass, Supplier<T> factory) {
this.extensionFactories.put(extensionClass, factory);
}
/**
* Parse an extension from the {@link CloudEventExtensions}, materializing the corresponding POJO.
*
* @param extensionClass the class implementing {@link Extension}
* @param eventExtensions the event extensions to read
* @param <T> the type of the extension
* @param extensionClass the class implementing {@link CloudEventExtension}
* @param eventExtensions the event extensions to read
* @return the parsed extension
*/
@SuppressWarnings("unchecked")
@Nullable
public <T extends Extension> T parseExtension(Class<T> extensionClass, CloudEventExtensions eventExtensions) {
public <T extends CloudEventExtension> T parseExtension(Class<T> extensionClass, CloudEventExtensions eventExtensions) {
Supplier<?> factory = extensionFactories.get(extensionClass);
if (factory != null) {
Extension ext = (Extension) factory.get();
CloudEventExtension ext = (CloudEventExtension) factory.get();
ext.readFrom(eventExtensions);
return (T) ext;
}

View File

@ -16,16 +16,22 @@
*/
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;
import io.cloudevents.types.Time;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.OffsetDateTime;
import io.cloudevents.SpecVersion;
import io.cloudevents.core.CloudEventUtils;
import io.cloudevents.core.impl.BaseCloudEventBuilder;
import io.cloudevents.rw.CloudEventContextReader;
import io.cloudevents.rw.CloudEventRWException;
import io.cloudevents.types.Time;
import static io.cloudevents.core.v03.CloudEventV03.*;
/**
* CloudEvent V0.3 builder.
@ -59,11 +65,10 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBui
protected void setAttributes(io.cloudevents.CloudEventContext event) {
CloudEventContextReader contextReader = CloudEventUtils.toContextReader(event);
if (event.getSpecVersion() == SpecVersion.V03) {
contextReader.readAttributes(this);
contextReader.readContext(this);
} else {
contextReader.readAttributes(new V1ToV03AttributesConverter(this));
contextReader.readContext(new V1ToV03AttributesConverter(this));
}
contextReader.readExtensions(this);
}
public CloudEventBuilder withId(String id) {
@ -119,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
@ -139,65 +151,166 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBui
}
// Message impl
@Override
public CloudEventBuilder withAttribute(String name, String value) throws CloudEventRWException {
public CloudEventContextWriter withContextAttribute(String name, String value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case "id":
case ID:
withId(value);
return this;
case "source":
case SOURCE:
try {
withSource(new URI(value));
} catch (URISyntaxException e) {
throw CloudEventRWException.newInvalidAttributeValue("source", value, e);
throw CloudEventRWException.newInvalidAttributeValue(SOURCE, value, e);
}
return this;
case "type":
case TYPE:
withType(value);
return this;
case "datacontenttype":
case DATACONTENTTYPE:
withDataContentType(value);
return this;
case "datacontentencoding":
case DATACONTENTENCODING:
// No-op, this information is not saved in the event because it's useful only for parsing
return this;
case "schemaurl":
case SCHEMAURL:
try {
withSchemaUrl(new URI(value));
} catch (URISyntaxException e) {
throw CloudEventRWException.newInvalidAttributeValue("schemaurl", value, e);
throw CloudEventRWException.newInvalidAttributeValue(SCHEMAURL, value, e);
}
return this;
case "subject":
case SUBJECT:
withSubject(value);
return this;
case "time":
withTime(Time.parseTime("time", value));
case TIME:
withTime(Time.parseTime(TIME, value));
return this;
default:
withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeName(name);
}
@Override
public CloudEventBuilder withAttribute(String name, URI value) throws CloudEventRWException {
public CloudEventContextWriter withContextAttribute(String name, URI value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case "source":
case SOURCE:
withSource(value);
return this;
case "schemaurl":
case SCHEMAURL:
withDataSchema(value);
return this;
case ID:
case TYPE:
case DATACONTENTTYPE:
case DATACONTENTENCODING:
case SUBJECT:
case TIME:
throw CloudEventRWException.newInvalidAttributeType(name, URI.class);
default:
withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeType(name, URI.class);
}
@Override
public CloudEventBuilder withAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
if ("time".equals(name)) {
withTime(value);
return this;
public CloudEventContextWriter withContextAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case TIME:
withTime(value);
return this;
case SCHEMAURL:
case ID:
case TYPE:
case DATACONTENTTYPE:
case DATACONTENTENCODING:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, OffsetDateTime.class);
default:
withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Number value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case TIME:
case SCHEMAURL:
case ID:
case TYPE:
case DATACONTENTTYPE:
case DATACONTENTENCODING:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, Number.class);
default:
withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Integer value) throws CloudEventRWException
{
requireValidAttributeWrite(name);
switch (name) {
case TIME:
case SCHEMAURL:
case ID:
case TYPE:
case DATACONTENTTYPE:
case DATACONTENTENCODING:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, Integer.class);
default:
withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Boolean value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case TIME:
case SCHEMAURL:
case ID:
case TYPE:
case DATACONTENTTYPE:
case DATACONTENTENCODING:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, Boolean.class);
default:
withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, byte[] value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case TIME:
case SCHEMAURL:
case ID:
case TYPE:
case DATACONTENTTYPE:
case DATACONTENTENCODING:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, byte[].class);
default:
withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeType(name, OffsetDateTime.class);
}
}

View File

@ -20,7 +20,7 @@ import io.cloudevents.CloudEventData;
import io.cloudevents.SpecVersion;
import io.cloudevents.core.impl.BaseCloudEvent;
import io.cloudevents.lang.Nullable;
import io.cloudevents.rw.CloudEventAttributesWriter;
import io.cloudevents.rw.CloudEventContextWriter;
import io.cloudevents.rw.CloudEventRWException;
import java.net.URI;
@ -169,43 +169,44 @@ public final class CloudEventV03 extends BaseCloudEvent {
}
@Override
public void readAttributes(CloudEventAttributesWriter writer) throws CloudEventRWException {
writer.withAttribute(
public void readContext(CloudEventContextWriter writer) throws CloudEventRWException {
writer.withContextAttribute(
ID,
this.id
);
writer.withAttribute(
writer.withContextAttribute(
SOURCE,
this.source
);
writer.withAttribute(
writer.withContextAttribute(
TYPE,
this.type
);
if (this.datacontenttype != null) {
writer.withAttribute(
writer.withContextAttribute(
DATACONTENTTYPE,
this.datacontenttype
);
}
if (this.schemaurl != null) {
writer.withAttribute(
writer.withContextAttribute(
SCHEMAURL,
this.schemaurl
);
}
if (this.subject != null) {
writer.withAttribute(
writer.withContextAttribute(
SUBJECT,
this.subject
);
}
if (this.time != null) {
writer.withAttribute(
writer.withContextAttribute(
TIME,
this.time
);
}
this.readExtensions(writer);
}
@Override
@ -235,12 +236,12 @@ public final class CloudEventV03 extends BaseCloudEvent {
"id='" + id + '\'' +
", source=" + source +
", type='" + type + '\'' +
", datacontenttype='" + datacontenttype + '\'' +
", schemaurl=" + schemaurl +
", subject='" + subject + '\'' +
", time=" + time +
", data=" + getData() +
", extensions" + this.extensions +
((datacontenttype != null) ? ", datacontenttype='" + datacontenttype + '\'' : "") +
((schemaurl != null) ? ", schemaurl=" + schemaurl : "") +
((subject != null) ? ", subject='" + subject + '\'' : "") +
((time != null) ? ", time=" + time : "") +
((getData() != null) ? ", data=" + getData() : "") +
", extensions=" + this.extensions +
'}';
}
}

View File

@ -17,7 +17,7 @@
package io.cloudevents.core.v03;
import io.cloudevents.rw.CloudEventAttributesWriter;
import io.cloudevents.rw.CloudEventContextWriter;
import io.cloudevents.rw.CloudEventRWException;
import io.cloudevents.types.Time;
@ -25,7 +25,9 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.time.OffsetDateTime;
class V1ToV03AttributesConverter implements CloudEventAttributesWriter {
import static io.cloudevents.core.v1.CloudEventV1.*;
class V1ToV03AttributesConverter implements CloudEventContextWriter {
private final CloudEventBuilder builder;
@ -34,60 +36,114 @@ class V1ToV03AttributesConverter implements CloudEventAttributesWriter {
}
@Override
public V1ToV03AttributesConverter withAttribute(String name, String value) throws CloudEventRWException {
public CloudEventContextWriter withContextAttribute(String name, String value) throws CloudEventRWException {
switch (name) {
case "id":
case ID:
builder.withId(value);
return this;
case "source":
case SOURCE:
try {
builder.withSource(new URI(value));
} catch (URISyntaxException e) {
throw CloudEventRWException.newInvalidAttributeValue("source", value, e);
throw CloudEventRWException.newInvalidAttributeValue(SOURCE, value, e);
}
return this;
case "type":
case TYPE:
builder.withType(value);
return this;
case "datacontenttype":
case DATACONTENTTYPE:
builder.withDataContentType(value);
return this;
case "dataschema":
case DATASCHEMA:
try {
builder.withSchemaUrl(new URI(value));
} catch (URISyntaxException e) {
throw CloudEventRWException.newInvalidAttributeValue("dataschema", value, e);
throw CloudEventRWException.newInvalidAttributeValue(DATASCHEMA, value, e);
}
return this;
case "subject":
case SUBJECT:
builder.withSubject(value);
return this;
case "time":
builder.withTime(Time.parseTime("time", value));
case TIME:
builder.withTime(Time.parseTime(TIME, value));
return this;
default:
builder.withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeName(name);
}
@Override
public V1ToV03AttributesConverter withAttribute(String name, URI value) throws CloudEventRWException {
public CloudEventContextWriter withContextAttribute(String name, URI value) throws CloudEventRWException {
switch (name) {
case "source":
case SOURCE:
builder.withSource(value);
return this;
case "dataschema":
case DATASCHEMA:
builder.withSchemaUrl(value);
return this;
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
case TIME:
throw CloudEventRWException.newInvalidAttributeType(name, URI.class);
default:
builder.withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeType(name, URI.class);
}
@Override
public V1ToV03AttributesConverter withAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
if ("time".equals(name)) {
builder.withTime(value);
return this;
public CloudEventContextWriter withContextAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
switch (name) {
case TIME:
builder.withTime(value);
return this;
case SOURCE:
case DATASCHEMA:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
throw CloudEventRWException.newInvalidAttributeType(name, OffsetDateTime.class);
default:
builder.withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Number value) throws CloudEventRWException {
switch (name) {
case TIME:
case SOURCE:
case DATASCHEMA:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
throw CloudEventRWException.newInvalidAttributeType(name, Number.class);
default:
builder.withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Boolean value) throws CloudEventRWException {
switch (name) {
case TIME:
case SOURCE:
case DATASCHEMA:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
throw CloudEventRWException.newInvalidAttributeType(name, Boolean.class);
default:
builder.withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeType(name, OffsetDateTime.class);
}
}

View File

@ -21,7 +21,10 @@ 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;
import io.cloudevents.types.Time;
@ -29,6 +32,8 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.time.OffsetDateTime;
import static io.cloudevents.core.v1.CloudEventV1.*;
/**
* CloudEvent V1.0 builder.
*
@ -61,11 +66,10 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBui
protected void setAttributes(io.cloudevents.CloudEventContext event) {
CloudEventContextReader contextReader = CloudEventUtils.toContextReader(event);
if (event.getSpecVersion() == SpecVersion.V1) {
contextReader.readAttributes(this);
contextReader.readContext(this);
} else {
contextReader.readAttributes(new V03ToV1AttributesConverter(this));
contextReader.readContext(new V03ToV1AttributesConverter(this));
}
contextReader.readExtensions(this);
}
public CloudEventBuilder withId(String id) {
@ -108,16 +112,24 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBui
@Override
public CloudEvent build() {
if (id == null) {
throw createMissingAttributeException(CloudEventV1.ID);
throw createMissingAttributeException(ID);
}
if (source == null) {
throw createMissingAttributeException(CloudEventV1.SOURCE);
throw createMissingAttributeException(SOURCE);
}
if (type == null) {
throw createMissingAttributeException(CloudEventV1.TYPE);
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
@ -138,60 +150,157 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBui
// Message impl
@Override
public CloudEventBuilder withAttribute(String name, String value) throws CloudEventRWException {
public CloudEventContextWriter withContextAttribute(String name, String value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case CloudEventV1.ID:
case ID:
withId(value);
return this;
case CloudEventV1.SOURCE:
case SOURCE:
try {
withSource(new URI(value));
} catch (URISyntaxException e) {
throw CloudEventRWException.newInvalidAttributeValue(CloudEventV1.SOURCE, value, e);
throw CloudEventRWException.newInvalidAttributeValue(SOURCE, value, e);
}
return this;
case CloudEventV1.TYPE:
case TYPE:
withType(value);
return this;
case CloudEventV1.DATACONTENTTYPE:
case DATACONTENTTYPE:
withDataContentType(value);
return this;
case CloudEventV1.DATASCHEMA:
case DATASCHEMA:
try {
withDataSchema(new URI(value));
} catch (URISyntaxException e) {
throw CloudEventRWException.newInvalidAttributeValue(CloudEventV1.DATASCHEMA, value, e);
throw CloudEventRWException.newInvalidAttributeValue(DATASCHEMA, value, e);
}
return this;
case CloudEventV1.SUBJECT:
case SUBJECT:
withSubject(value);
return this;
case CloudEventV1.TIME:
withTime(Time.parseTime(CloudEventV1.TIME, value));
case TIME:
withTime(Time.parseTime(TIME, value));
return this;
default:
withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeName(name);
}
@Override
public CloudEventBuilder withAttribute(String name, URI value) throws CloudEventRWException {
public CloudEventContextWriter withContextAttribute(String name, URI value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case CloudEventV1.SOURCE:
case SOURCE:
withSource(value);
return this;
case CloudEventV1.DATASCHEMA:
case DATASCHEMA:
withDataSchema(value);
return this;
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
case TIME:
throw CloudEventRWException.newInvalidAttributeType(name, URI.class);
default:
withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeType(name, URI.class);
}
@Override
public CloudEventBuilder withAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
if (CloudEventV1.TIME.equals(name)) {
withTime(value);
return this;
public CloudEventContextWriter withContextAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case TIME:
withTime(value);
return this;
case DATASCHEMA:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, OffsetDateTime.class);
default:
withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Number value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case TIME:
case DATASCHEMA:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, Number.class);
default:
withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Integer value) throws CloudEventRWException
{
requireValidAttributeWrite(name);
switch (name) {
case TIME:
case DATASCHEMA:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, Integer.class);
default:
withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Boolean value) throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case TIME:
case DATASCHEMA:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, Boolean.class);
default:
withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, byte[] value)
throws CloudEventRWException {
requireValidAttributeWrite(name);
switch (name) {
case TIME:
case DATASCHEMA:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
case SOURCE:
throw CloudEventRWException.newInvalidAttributeType(name, byte[].class);
default:
withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeType(name, OffsetDateTime.class);
}
}

View File

@ -19,7 +19,7 @@ package io.cloudevents.core.v1;
import io.cloudevents.CloudEventData;
import io.cloudevents.SpecVersion;
import io.cloudevents.core.impl.BaseCloudEvent;
import io.cloudevents.rw.CloudEventAttributesWriter;
import io.cloudevents.rw.CloudEventContextWriter;
import io.cloudevents.rw.CloudEventRWException;
import java.net.URI;
@ -156,43 +156,44 @@ public final class CloudEventV1 extends BaseCloudEvent {
}
@Override
public void readAttributes(CloudEventAttributesWriter writer) throws CloudEventRWException {
writer.withAttribute(
public void readContext(CloudEventContextWriter writer) throws CloudEventRWException {
writer.withContextAttribute(
ID,
this.id
);
writer.withAttribute(
writer.withContextAttribute(
SOURCE,
this.source
);
writer.withAttribute(
writer.withContextAttribute(
TYPE,
this.type
);
if (this.datacontenttype != null) {
writer.withAttribute(
writer.withContextAttribute(
DATACONTENTTYPE,
this.datacontenttype
);
}
if (this.dataschema != null) {
writer.withAttribute(
writer.withContextAttribute(
DATASCHEMA,
this.dataschema
);
}
if (this.subject != null) {
writer.withAttribute(
writer.withContextAttribute(
SUBJECT,
this.subject
);
}
if (this.time != null) {
writer.withAttribute(
writer.withContextAttribute(
TIME,
this.time
);
}
this.readExtensions(writer);
}
@Override
@ -222,11 +223,11 @@ public final class CloudEventV1 extends BaseCloudEvent {
"id='" + id + '\'' +
", source=" + source +
", type='" + type + '\'' +
", datacontenttype='" + datacontenttype + '\'' +
", dataschema=" + dataschema +
", subject='" + subject + '\'' +
", time=" + time +
", data=" + getData() +
((datacontenttype != null) ? ", datacontenttype='" + datacontenttype + '\'' : "") +
((dataschema != null) ? ", dataschema=" + dataschema : "") +
((subject != null) ? ", subject='" + subject + '\'' : "") +
((time != null) ? ", time=" + time : "") +
((getData() != null) ? ", data=" + getData() : "") +
", extensions=" + this.extensions +
'}';
}

View File

@ -17,7 +17,7 @@
package io.cloudevents.core.v1;
import io.cloudevents.rw.CloudEventAttributesWriter;
import io.cloudevents.rw.CloudEventContextWriter;
import io.cloudevents.rw.CloudEventRWException;
import io.cloudevents.types.Time;
@ -25,7 +25,9 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.time.OffsetDateTime;
class V03ToV1AttributesConverter implements CloudEventAttributesWriter {
import static io.cloudevents.core.v03.CloudEventV03.*;
class V03ToV1AttributesConverter implements CloudEventContextWriter {
private final CloudEventBuilder builder;
@ -34,60 +36,114 @@ class V03ToV1AttributesConverter implements CloudEventAttributesWriter {
}
@Override
public V03ToV1AttributesConverter withAttribute(String name, String value) throws CloudEventRWException {
public CloudEventContextWriter withContextAttribute(String name, String value) throws CloudEventRWException {
switch (name) {
case "id":
case ID:
builder.withId(value);
return this;
case "source":
case SOURCE:
try {
builder.withSource(new URI(value));
} catch (URISyntaxException e) {
throw CloudEventRWException.newInvalidAttributeValue("source", value, e);
throw CloudEventRWException.newInvalidAttributeValue(SOURCE, value, e);
}
return this;
case "type":
case TYPE:
builder.withType(value);
return this;
case "datacontenttype":
case DATACONTENTTYPE:
builder.withDataContentType(value);
return this;
case "schemaurl":
case SCHEMAURL:
try {
builder.withDataSchema(new URI(value));
} catch (URISyntaxException e) {
throw CloudEventRWException.newInvalidAttributeValue("dataschema", value, e);
throw CloudEventRWException.newInvalidAttributeValue(SCHEMAURL, value, e);
}
return this;
case "subject":
case SUBJECT:
builder.withSubject(value);
return this;
case "time":
builder.withTime(Time.parseTime("time", value));
case TIME:
builder.withTime(Time.parseTime(TIME, value));
return this;
default:
builder.withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeName(name);
}
@Override
public V03ToV1AttributesConverter withAttribute(String name, URI value) throws CloudEventRWException {
public CloudEventContextWriter withContextAttribute(String name, URI value) throws CloudEventRWException {
switch (name) {
case "source":
case SOURCE:
builder.withSource(value);
return this;
case "schemaurl":
case SCHEMAURL:
builder.withDataSchema(value);
return this;
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
case TIME:
throw CloudEventRWException.newInvalidAttributeType(name, URI.class);
default:
builder.withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeType(name, URI.class);
}
@Override
public V03ToV1AttributesConverter withAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
if ("time".equals(name)) {
builder.withTime(value);
return this;
public CloudEventContextWriter withContextAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
switch (name) {
case TIME:
builder.withTime(value);
return this;
case SOURCE:
case SCHEMAURL:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
throw CloudEventRWException.newInvalidAttributeType(name, OffsetDateTime.class);
default:
builder.withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Number value) throws CloudEventRWException {
switch (name) {
case TIME:
case SOURCE:
case SCHEMAURL:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
throw CloudEventRWException.newInvalidAttributeType(name, Number.class);
default:
builder.withExtension(name, value);
return this;
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Boolean value) throws CloudEventRWException {
switch (name) {
case TIME:
case SOURCE:
case SCHEMAURL:
case ID:
case TYPE:
case DATACONTENTTYPE:
case SUBJECT:
throw CloudEventRWException.newInvalidAttributeType(name, Boolean.class);
default:
builder.withExtension(name, value);
return this;
}
throw CloudEventRWException.newInvalidAttributeType(name, OffsetDateTime.class);
}
}

View File

@ -14,15 +14,20 @@
* limitations under the License.
*
*/
package io.cloudevents.http.restful.ws.impl;
package io.cloudevents.core.validator;
import io.cloudevents.CloudEvent;
public class Utils {
public static boolean isCloudEventEntity(Object obj) {
return obj != null && CloudEvent.class.isAssignableFrom(obj.getClass());
}
/**
* @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);
}

View File

@ -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())

View File

@ -1,15 +1,16 @@
package io.cloudevents.core.impl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import io.cloudevents.core.extensions.DistributedTracingExtension;
import io.cloudevents.core.test.Data;
import org.junit.jupiter.api.Test;
import java.util.Objects;
import static io.cloudevents.core.test.Data.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
public class BaseCloudEventBuilderTest {
@ -49,17 +50,23 @@ public class BaseCloudEventBuilderTest {
}
@Test
public void testLongExtensionName() {
Exception exception = assertThrows(RuntimeException.class, () -> {
CloudEvent cloudEvent = CloudEventBuilder.v1(Data.V1_WITH_JSON_DATA_WITH_EXT)
public void testLongExtensionNameV1() {
assertDoesNotThrow(() -> {
CloudEventBuilder.v1(Data.V1_WITH_JSON_DATA_WITH_EXT)
.withExtension("thisextensionnameistoolong", "")
.build();
});
String expectedMessage = "Invalid extensions name: thisextensionnameistoolong";
String actualMessage = exception.getMessage();
assertTrue(actualMessage.contains(expectedMessage));
}
@Test
public void testLongExtensionNameV03() {
assertDoesNotThrow(() -> {
CloudEventBuilder.v03(Data.V1_WITH_JSON_DATA_WITH_EXT)
.withExtension("thisextensionnameistoolong", "")
.build();
});
}
@Test
public void testInvalidExtensionName() {
Exception exception = assertThrows(RuntimeException.class, () -> {
@ -72,4 +79,64 @@ public class BaseCloudEventBuilderTest {
assertTrue(actualMessage.contains(expectedMessage));
}
@Test
public void testBinaryExtension() {
final String EXT_NAME = "verifyme";
CloudEvent given = CloudEventBuilder.v1(Data.V1_MIN)
.withExtension(EXT_NAME, Data.BINARY_VALUE)
.build();
// Sanity
assertNotNull(given);
// Did the extension stick
assertTrue(given.getExtensionNames().contains(EXT_NAME));
assertNotNull(given.getExtension(EXT_NAME));
// Does the extension have the right value
assertEquals(Data.BINARY_VALUE, given.getExtension(EXT_NAME));
}
@Test
public void withoutDataRemovesDataAttributeFromCopiedCloudEvent() {
CloudEvent original = CloudEventBuilder.v1(Data.V1_WITH_JSON_DATA_WITH_EXT).build();
CloudEvent copy = CloudEventBuilder.v1(original).withoutData().build();
assertAll(
() -> assertThat(copy.getData()).isNull(),
() -> assertThat(copy.getDataContentType()).isEqualTo(DATACONTENTTYPE_JSON),
() -> assertThat(copy.getDataSchema()).isEqualTo(DATASCHEMA)
);
}
@Test
public void withoutDataContentTypeRemovesDataContentTypeAttributeFromCopiedCloudEvent() {
CloudEvent original = CloudEventBuilder.v1(Data.V1_WITH_JSON_DATA_WITH_EXT).build();
CloudEvent copy = CloudEventBuilder.v1(original).withoutDataContentType().build();
assertAll(
() -> assertThat(Objects.requireNonNull(copy.getData()).toBytes()).isEqualTo(DATA_JSON_SERIALIZED),
() -> assertThat(copy.getDataContentType()).isNull(),
() -> assertThat(copy.getDataSchema()).isEqualTo(DATASCHEMA)
);
}
@Test
public void withoutDataSchemaRemovesDataSchemaAttributeFromCopiedCloudEvent() {
CloudEvent original = CloudEventBuilder.v1(Data.V1_WITH_JSON_DATA_WITH_EXT).build();
CloudEvent copy = CloudEventBuilder.v1(original).withoutDataSchema().build();
assertAll(
() -> assertThat(Objects.requireNonNull(copy.getData()).toBytes()).isEqualTo(DATA_JSON_SERIALIZED),
() -> assertThat(copy.getDataContentType()).isEqualTo(DATACONTENTTYPE_JSON),
() -> assertThat(copy.getDataSchema()).isNull()
);
}
}

View File

@ -91,4 +91,28 @@ public class CloudEventImplTest {
);
}
@Test
public void testToStringV1() {
CloudEvent event = CloudEventBuilder.v1()
.withId(ID)
.withType(TYPE)
.withSource(SOURCE)
.build();
assertThat(event.toString())
.doesNotContain("time");
}
@Test
public void testToStringV03() {
CloudEvent event = CloudEventBuilder.v03()
.withId(ID)
.withType(TYPE)
.withSource(SOURCE)
.build();
assertThat(event.toString())
.doesNotContain("time");
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.impl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
public class StringUtilsTest {
@ParameterizedTest
@MethodSource("startsWithIgnoreCaseArgs")
public void startsWithIgnoreCase(final String s, final String prefix, final boolean expected) {
Assertions.assertThat(StringUtils.startsWithIgnoreCase(s, prefix)).isEqualTo(expected);
}
private static Stream<Arguments> startsWithIgnoreCaseArgs() {
return Stream.of(
Arguments.of("s", "s", true),
Arguments.of("sa", "S", true),
Arguments.of("saS", "As", false),
Arguments.of("sasso", "SASO", false),
Arguments.of("sasso", "SaSsO", true)
);
}
}

View File

@ -27,6 +27,25 @@ class MessageUtilsTest {
.isEqualTo(CloudEventRWException.CloudEventRWExceptionKind.UNKNOWN_ENCODING);
}
/**
* Verify an exception is thrown if an unsupported
* application/cloudevents content-type family is
* received.
*/
@ParameterizedTest
@MethodSource
void testBadContentTypes(String contentType) {
CloudEventRWException exception = assertThrows(CloudEventRWException.class, () ->
{
parseStructuredOrBinaryMessage(() -> contentType, eventFormat -> null, () -> "1.0", specVersion -> null);
});
assertThat(exception.getKind()).isEqualTo(CloudEventRWException.CloudEventRWExceptionKind.UNKNOWN_ENCODING);
}
@Test
void testParseStructuredOrBinaryMessage_StructuredMode() {
MessageUtils.parseStructuredOrBinaryMessage(() -> "application/cloudevents+csv;",
@ -54,4 +73,11 @@ class MessageUtilsTest {
);
}
private static Stream<Arguments> testBadContentTypes() {
return Stream.of(
Arguments.of("application/cloudevents"),
Arguments.of("application/cloudevents+morse")
);
}
}

View File

@ -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)

View File

@ -34,24 +34,21 @@ import java.util.Map;
public class MockBinaryMessageWriter extends BaseBinaryMessageReader implements MessageReader, CloudEventContextReader, CloudEventWriterFactory<MockBinaryMessageWriter, MockBinaryMessageWriter>, CloudEventWriter<MockBinaryMessageWriter> {
private SpecVersion version;
private Map<String, Object> attributes;
private Map<String, Object> context;
private CloudEventData data;
private Map<String, Object> extensions;
public MockBinaryMessageWriter(SpecVersion version, Map<String, Object> attributes, CloudEventData data, Map<String, Object> extensions) {
public MockBinaryMessageWriter(SpecVersion version, Map<String, Object> context, CloudEventData data) {
this.version = version;
this.attributes = attributes;
this.context = context;
this.data = data;
this.extensions = extensions;
}
public MockBinaryMessageWriter(SpecVersion version, Map<String, Object> attributes, byte[] data, Map<String, Object> extensions) {
this(version, attributes, BytesCloudEventData.wrap(data), extensions);
public MockBinaryMessageWriter(SpecVersion version, Map<String, Object> context, byte[] data) {
this(version, context, BytesCloudEventData.wrap(data));
}
public MockBinaryMessageWriter() {
this.attributes = new HashMap<>();
this.extensions = new HashMap<>();
this.context = new HashMap<>();
}
public MockBinaryMessageWriter(CloudEvent event) {
@ -67,47 +64,14 @@ public class MockBinaryMessageWriter extends BaseBinaryMessageReader implements
throw new IllegalStateException("MockBinaryMessage is empty");
}
CloudEventWriter<V> visitor = writerFactory.create(version);
this.readAttributes(visitor);
this.readExtensions(visitor);
CloudEventWriter<V> writer = writerFactory.create(version);
this.readContext(writer);
if (this.data != null) {
return visitor.end(mapper.map(this.data));
return writer.end(mapper.map(this.data));
}
return visitor.end();
}
@Override
public void readAttributes(CloudEventAttributesWriter writer) throws CloudEventRWException, IllegalStateException {
for (Map.Entry<String, Object> e : this.attributes.entrySet()) {
if (e.getValue() instanceof String) {
writer.withAttribute(e.getKey(), (String) e.getValue());
} else if (e.getValue() instanceof OffsetDateTime) {
writer.withAttribute(e.getKey(), (OffsetDateTime) e.getValue());
} else if (e.getValue() instanceof URI) {
writer.withAttribute(e.getKey(), (URI) e.getValue());
} else {
// This should never happen because we build that map only through our builders
throw new IllegalStateException("Illegal value inside attributes map: " + e);
}
}
}
@Override
public void readExtensions(CloudEventExtensionsWriter writer) throws CloudEventRWException, IllegalStateException {
for (Map.Entry<String, Object> entry : this.extensions.entrySet()) {
if (entry.getValue() instanceof String) {
writer.withExtension(entry.getKey(), (String) entry.getValue());
} else if (entry.getValue() instanceof Number) {
writer.withExtension(entry.getKey(), (Number) entry.getValue());
} else if (entry.getValue() instanceof Boolean) {
writer.withExtension(entry.getKey(), (Boolean) entry.getValue());
} else {
// This should never happen because we build that map only through our builders
throw new IllegalStateException("Illegal value inside extensions map: " + entry);
}
}
return writer.end();
}
@Override
@ -121,46 +85,60 @@ public class MockBinaryMessageWriter extends BaseBinaryMessageReader implements
return this;
}
@Override
public MockBinaryMessageWriter withAttribute(String name, String value) throws CloudEventRWException {
this.attributes.put(name, value);
return this;
}
@Override
public MockBinaryMessageWriter withAttribute(String name, URI value) throws CloudEventRWException {
this.attributes.put(name, value);
return this;
}
@Override
public MockBinaryMessageWriter withAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
this.attributes.put(name, value);
return this;
}
@Override
public MockBinaryMessageWriter withExtension(String name, String value) throws CloudEventRWException {
this.extensions.put(name, value);
return this;
}
@Override
public MockBinaryMessageWriter withExtension(String name, Number value) throws CloudEventRWException {
this.extensions.put(name, value);
return this;
}
@Override
public MockBinaryMessageWriter withExtension(String name, Boolean value) throws CloudEventRWException {
this.extensions.put(name, value);
return this;
}
@Override
public MockBinaryMessageWriter create(SpecVersion version) {
this.version = version;
return this;
}
@Override
public void readContext(CloudEventContextWriter writer) throws CloudEventRWException {
for (Map.Entry<String, Object> entry : this.context.entrySet()) {
if (entry.getValue() instanceof String) {
writer.withContextAttribute(entry.getKey(), (String) entry.getValue());
} else if (entry.getValue() instanceof OffsetDateTime) {
writer.withContextAttribute(entry.getKey(), (OffsetDateTime) entry.getValue());
} else if (entry.getValue() instanceof URI) {
writer.withContextAttribute(entry.getKey(), (URI) entry.getValue());
} else if (entry.getValue() instanceof Number) {
writer.withContextAttribute(entry.getKey(), (Number) entry.getValue());
} else if (entry.getValue() instanceof Boolean) {
writer.withContextAttribute(entry.getKey(), (Boolean) entry.getValue());
} else {
// This should never happen because we build that map only through our builders
throw new IllegalStateException("Illegal value inside context map: " + entry);
}
}
}
@Override
public CloudEventContextWriter withContextAttribute(String name, String value) throws CloudEventRWException {
this.context.put(name, value);
return this;
}
@Override
public CloudEventContextWriter withContextAttribute(String name, URI value) throws CloudEventRWException {
this.context.put(name, value);
return this;
}
@Override
public CloudEventContextWriter withContextAttribute(String name, OffsetDateTime value) throws CloudEventRWException {
this.context.put(name, value);
return this;
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Number value) throws CloudEventRWException {
this.context.put(name, value);
return this;
}
@Override
public CloudEventContextWriter withContextAttribute(String name, Boolean value) throws CloudEventRWException {
this.context.put(name, value);
return this;
}
}

View File

@ -41,12 +41,12 @@ public class MockStructuredMessageReader extends BaseStructuredMessageReader imp
}
@Override
public <T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException {
public <T> T read(StructuredMessageWriter<T> writer) throws CloudEventRWException, IllegalStateException {
if (this.format == null) {
throw new IllegalStateException("MockStructuredMessage is empty");
}
return visitor.setEvent(this.format, this.payload);
return writer.setEvent(this.format, this.payload);
}
@Override

View File

@ -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() {

View File

@ -36,4 +36,9 @@ public class EventFormatProviderTest {
.isInstanceOf(CSVFormat.class);
}
@Test
void listTypes() {
assertThat(EventFormatProvider.getInstance().getContentTypes()).hasSize(1);
}
}

View File

@ -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");
}
}
}

View File

@ -21,7 +21,9 @@ import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
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;
@ -38,9 +40,10 @@ 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()
.withId(ID)
@ -108,6 +111,23 @@ public class Data {
.withTime(TIME)
.build();
public static final CloudEvent V1_WITH_BINARY_EXT = CloudEventBuilder.v1()
.withId(ID)
.withType(TYPE)
.withSource(SOURCE)
.withExtension("binary", BINARY_VALUE)
.build();
public static final CloudEvent V1_WITH_NUMERIC_EXT = CloudEventBuilder.v1()
.withId(ID)
.withType(TYPE)
.withSource(SOURCE)
.withExtension("integer", 42)
.withExtension("decimal", new BigDecimal("42.42"))
.withExtension("float", 4.2f)
.withExtension("long", new Long(4200))
.build();
public static final CloudEvent V03_MIN = CloudEventBuilder.v03(V1_MIN).build();
public static final CloudEvent V03_WITH_JSON_DATA = CloudEventBuilder.v03(V1_WITH_JSON_DATA).build();
public static final CloudEvent V03_WITH_JSON_DATA_WITH_EXT = CloudEventBuilder.v03(V1_WITH_JSON_DATA_WITH_EXT).build();
@ -137,6 +157,18 @@ public class Data {
);
}
/**
* Due to the nature of CE there are scenarios where an event might be serialized
* in such a fashion that it can not be deserialized while retaining the orginal
* type information, this varies from format-2-format
*/
public static Stream<CloudEvent> v1NonRoundTripEvents() {
return Stream.of(
Data.V1_WITH_BINARY_EXT
);
}
public static Stream<CloudEvent> v03Events() {
return Stream.of(
Data.V03_MIN,

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -0,0 +1 @@
io.cloudevents.core.test.CloudEventCustomValidator

View File

@ -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.7.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,30 +203,32 @@ 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.4.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.10.10)
mini_portile2 (~> 2.4.0)
nokogiri (1.18.3)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
octokit (4.19.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (3.1.1)
racc (1.8.1)
rake (13.0.1)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.4)
rexml (3.3.9)
rouge (3.23.0)
ruby-enum (0.8.0)
i18n
@ -248,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

View File

@ -4,19 +4,19 @@
title: Java SDK for CloudEvents
remote_theme: pmarsceill/just-the-docs
plugins:
- jemoji
- jemoji
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"
aux_links:
"GitHub Repository":
- "https://github.com/cloudevents/sdk-java"
"CloudEvents home":
- "https://cloudevents.io/"
"GitHub Repository":
- "https://github.com/cloudevents/sdk-java"
"CloudEvents home":
- "https://cloudevents.io/"

47
docs/amqp-proton.md Normal file
View File

@ -0,0 +1,47 @@
---
title: CloudEvents AMQP Proton
nav_order: 5
---
# CloudEvents AMQP Proton
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-amqp-proton.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-amqp-proton)
This module implements `MessageReader` and `MessageWriter` using the Qpid Proton
library. It can be used with Qpid Proton or any integrations based on Qpid
Proton (e.g vertx-proton).
For Maven based projects, use the following to configure the `proton` AMQP
binding for CloudEvents:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-amqp-proton</artifactId>
<version>4.0.1</version>
</dependency>
```
## Sending and Receiving CloudEvents
To send and receive CloudEvents we use `MessageWriter` and `MessageReader`,
respectively. This module offers factory methods for creation of those in
`ProtonAmqpMessageFactory`.
```java
public class ProtonAmqpMessageFactory {
public static MessageReader createReader(final Message message);
public static MessageReader createReader(final String contentType, final ApplicationProperties props, @Nullable final Section body);
public static MessageWriter createWriter();
}
```
## Examples:
The example uses the `vertx-proton` integration to send/receive CloudEvent
messages over AMQP:
- [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)

65
docs/api.md Normal file
View File

@ -0,0 +1,65 @@
---
title: CloudEvents API
nav_order: 2
---
# CloudEvents API
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-api.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-api)
This module contains the interfaces to represent `CloudEvent` in memory and to
read and write an object using as CloudEvent.
For Maven based projects, use the following dependency:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-api</artifactId>
<version>4.0.1</version>
</dependency>
```
## `CloudEvent` hierarchy
`CloudEvent` is the main interface representing a read-only CloudEvent in-memory
representation. A `CloudEvent` is composed by its context attributes, including
the predefined attributes and the extensions, and the data.
![](api.png)
`CloudEventData` is an abstraction to allow carrying any kind of data payload
inside a `CloudEvent`, while enforcing the ability of convert such data to
`[]byte`, ultimately used to send `CloudEvent` on the wire.
## Reader and Writer
The package `io.cloudevents.rw` contains the interfaces to read and write
objects as CloudEvents.
![](rw.png)
In other words, you can use these interfaces to perform an unstructured
read/write of an entity as CloudEvent. For example, an HTTP server request in
binary mode containing a valid CloudEvent can be translated to a
`CloudEventReader`. Similarly, an HTTP server response can be written as a
CloudEvent, hence an eventual response builder could implement
`CloudEventWriter`.
`CloudEventReader` and `CloudEventWriter` implementations doesn't have any
particular knowledge about specification version, difference between attributes
and extensions, and so on. Their only concern is how to read and write context
attributes and data back and forth to the "CloudEvents type system", as defined
in the package `io.cloudevents`.
A 3rd party implementer can implement these interfaces directly in its
`CloudEvent` in order to customize/implement efficiently the
marshalling/unmarshalling process. These interfaces are optional and, if your
`CloudEvent` doesn't implement it, a default implementation is provided by the
core module.
## Other interfaces
- `CloudEventExtension` represents a _materialized_ in-memory representation of a CloudEvent extension
- `SpecVersion` is an enum of CloudEvents' specification versions supported by this SDK version.

BIN
docs/api.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

49
docs/avro.md Normal file
View File

@ -0,0 +1,49 @@
---
title: CloudEvents Avro Compact
nav_order: 4
---
# CloudEvents Avro Compact
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-avro-compact.svg?color=green)](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.

115
docs/core.md Normal file
View File

@ -0,0 +1,115 @@
---
title: CloudEvents Core
nav_order: 3
---
# CloudEvents Core
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-core.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-core)
This package includes implementations and utilities to create and process
`CloudEvent` and interfaces to deal with Protocol Bindings and Event Formats.
For Maven based projects, use the following dependency:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-core</artifactId>
<version>4.0.1</version>
</dependency>
```
### Using `CloudEvent`s
To create an event, you can use the `CloudEventBuilder`:
```java
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import java.net.URI;
final CloudEvent event = CloudEventBuilder.v1()
.withId("000")
.withType("example.demo")
.withSource(URI.create("http://example.com"))
.withData("text/plain","Hello world!".getBytes("UTF-8"))
.build();
```
Now you can access the event context attributes and data:
```java
// Get an event attribute
String id = event.getId();
// Get an event extension
Object someExtension = event.getExtension("myextension");
// Retrieve the event data
CloudEventData data = event.getData();
```
### Creating and accessing to the event data
`CloudEventData` is an abstraction that can wrap any kind of data payload, while
still enforcing the conversion to bytes. To convert to bytes:
```java
// Convert the event data to bytes
byte[]bytes = cloudEventData.toBytes();
```
If you want to map the JSON event data to POJOs using Jackson, check out the
[CloudEvents Jackson documentation](json-jackson.md).
When building an event, you can wrap a POJO inside its payload using
`PojoCloudEventData`, providing the function to convert it back to bytes:
```java
CloudEventData pojoData = PojoCloudEventData.wrap(myPojo, myPojo::toBytes);
```
### Using Event Formats
The SDK implements
[Event Formats](https://github.com/cloudevents/spec/blob/v1.0/spec.md#event-format)
in various submodules, but keeping a single entrypoint for users who wants to
serialize/deserialize events back and forth to event formats.
To use an Event Format, you just need to add the event format implementation you
prefer as dependency in your project and the core module, through the
`ServiceLoader` mechanism, will load it into the classpath. For example, to use
the
[JSON event format](https://github.com/cloudevents/spec/blob/v1.0/json-format.md)
with Jackson, add `cloudevents-json-jackson` as a dependency and then using the
`EventFormatProvider`:
```java
import io.cloudevents.core.provider.EventFormatProvider;
import io.cloudevents.jackson.JsonFormat;
EventFormat format = EventFormatProvider
.getInstance()
.resolveFormat(JsonFormat.CONTENT_TYPE);
// Serialize event
byte[] serialized = format.serialize(event);
// Deserialize event
CloudEvent event = format.deserialize(bytes);
```
### Materialize an Extension
CloudEvent extensions can be materialized in their respective POJOs using the
`ExtensionProvider`:
```java
import io.cloudevents.core.extensions.DistributedTracingExtension;
import io.cloudevents.core.provider.ExtensionProvider;
DistributedTracingExtension dte = ExtensionProvider.getInstance()
.parseExtension(DistributedTracingExtension.class, event);
```

53
docs/http-basic.md Normal file
View File

@ -0,0 +1,53 @@
---
title: CloudEvents HTTP Basic
nav_order: 5
---
# Generic HTTP Protocol Binding
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-http-basic.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-http-basic)
This module is designed to be usable with various HTTP APIs.
There are also more specialized HTTP bindings:
- [`cloudevents-http-vertx`](http-vertx.md)
- [`cloudevents-http-restful-ws`](http-jakarta-restful-ws.md)
- [`cloudevents-spring`](spring.md)
Since this module is generic it doesn't offer optimal performance for all HTTP
implementations. For better performance consider implementing `MessageReader`
and `MessageWriter` that are tailored for specific HTTP implementation. As a
reference you can take aforementioned existing bindings.
For Maven based projects, use the following to configure the CloudEvents Generic
HTTP Transport:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-http-basic</artifactId>
<version>4.0.1</version>
</dependency>
```
## Sending and Receiving CloudEvents
To send and receive CloudEvents we use `MessageWriter` and `MessageReader`,
respectively. This module offers factory methods for creation of those in
`HttpMessageFactory`.
```java
public class HttpMessageFactory {
public static MessageReader createReader(Consumer<BiConsumer<String,String>> forEachHeader, byte[] body);
public static MessageReader createReader(Map<String,String> headers, byte[] body);
public static MessageReader createReaderFromMultimap(Map<String,List<String>> headers, byte[] body);
public static MessageWriter createWriter(BiConsumer<String, String> putHeader, Consumer<byte[]> sendBody);
}
```
## Examples
- [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)

View File

@ -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
[![Javadocs](https://www.javadoc.io/badge/io.cloudevents/cloudevents-http-restful-ws.svg?color=green)](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)

View File

@ -0,0 +1,122 @@
---
title: CloudEvents HTTP Jakarta RESTful Web Services
nav_order: 5
---
# HTTP Protocol Binding for Jakarta EE8 - RESTful Web Services
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-http-restful-ws.svg?color=green)](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 for Jakarta EE 8:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-http-restful-ws</artifactId>
<version>4.0.1</version>
</dependency>
```
This integration is tested with Jersey, RestEasy & Spring Boot Jersey.
## 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 javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.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 javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.HttpHeaders;
import javax.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);
}
}
```
## Examples
- [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)

98
docs/http-vertx.md Normal file
View File

@ -0,0 +1,98 @@
---
title: CloudEvents HTTP Vert.x
nav_order: 5
---
# HTTP Protocol Binding for Eclipse Vert.x
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-http-vertx.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-http-vertx)
For Maven based projects, use the following to configure the CloudEvents Vertx
HTTP Transport:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-http-vertx</artifactId>
<version>4.0.1</version>
</dependency>
```
## Receiving CloudEvents
Assuming you have in classpath [`cloudevents-json-jackson`](json-jackson.md),
below is a sample on how to read and write CloudEvents:
```java
import io.cloudevents.http.vertx.VertxMessageFactory;
import io.cloudevents.core.message.StructuredMessageReader;
import io.cloudevents.CloudEvent;
import io.vertx.core.AbstractVerticle;
public class CloudEventServerVerticle extends AbstractVerticle {
public void start() {
vertx.createHttpServer()
.requestHandler(req -> {
VertxMessageFactory.createReader(req)
.onSuccess(messageReader -> {
CloudEvent event = messageReader.toEvent();
// Echo the message, as structured mode
VertxMessageFactory
.createWriter(req.response())
.writeStructured(event, "application/cloudevents+json");
})
.onFailure(t -> req.response().setStatusCode(500).end());
})
.listen(8080)
.onSuccess(server ->
System.out.println("Server started on port " + server.actualPort())
).onFailure(t -> {
System.out.println("Error starting the server");
serverResult.cause().printStackTrace();
});
}
}
```
## Sending CloudEvents
Below is a sample on how to use the client to send and receive a CloudEvent:
```java
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import io.cloudevents.http.vertx.VertxMessageFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.client.WebClient;
import java.net.URI;
public class CloudEventClientVerticle extends AbstractVerticle {
public void start() {
WebClient client = WebClient.create(vertx);
CloudEvent reqEvent = CloudEventBuilder.v1()
.withId("hello")
.withType("example.vertx")
.withSource(URI.create("http://localhost"))
.build();
VertxMessageFactory
.createWriter(client.postAbs("http://localhost:8080"))
.writeBinary(reqEvent)
.onSuccess(response -> {
CloudEvent responseEvent = VertxMessageFactory
.createReader(response)
.toEvent();
})
.onFailure(Throwable::printStackTrace);
}
}
```
## Examples:
- [Vert.x Client and Server](https://github.com/cloudevents/sdk-java/tree/main/examples/vertx)

View File

@ -5,100 +5,133 @@ nav_order: 1
# Java SDK for CloudEvents
A Java API for the [CloudEvents specification](https://github.com/cloudevents/spec)
1. [Supported Specification Features](#supported-specification-features)
1. [Modules](#modules)
1. [Introduction](#introduction)
1. [Supported features](#supported-features)
1. [Get Started](#get-started)
1. [Use an event format](#use-an-event-format)
1. [Use a protocol binding](#use-a-protocol-binding)
1. [Modules](#modules)
## Supported Specification Features
## Introduction
Supported features of the specification:
The Java SDK for CloudEvents is a collection of Java libraries to adopt
CloudEvents in your Java application.
| | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/tree/v1.0) |
| -------- | -- | -- |
| CloudEvents Core | :heavy_check_mark: | :heavy_check_mark: |
| AMQP Protocol Binding | :x: | :x: |
| AVRO Event Format | :x: | :x: |
| HTTP Protocol Binding | :heavy_check_mark: | :heavy_check_mark: |
| - [Vert.x](https://github.com/cloudevents/sdk-java/tree/master/http/vertx) | :heavy_check_mark: | :heavy_check_mark: |
| - [Jakarta Restful WS](https://github.com/cloudevents/sdk-java/tree/master/http/restful-ws) | :heavy_check_mark: | :heavy_check_mark: |
| JSON Event Format | :heavy_check_mark: | :heavy_check_mark: |
| - [Jackson](https://github.com/cloudevents/sdk-java/tree/master/formats/json-jackson) | :heavy_check_mark: | :heavy_check_mark: |
| [Kafka Protocol Binding](https://github.com/cloudevents/sdk-java/tree/master/kafka) | :heavy_check_mark: | :heavy_check_mark: |
| MQTT Protocol Binding | :x: | :x: |
| NATS Protocol Binding | :x: | :x: |
| Web hook | :x: | :x: |
Using the Java SDK you can:
## Modules
- Access, create and manipulate `CloudEvent` inside your application.
- Serialize and deserialize `CloudEvent` back and forth using the _CloudEvents
Event Format_, like Json.
- Read and write `CloudEvent` back and forth to HTTP, Kafka, AMQP using the
_CloudEvents Protocol Binding_ implementations we provide for a wide range
of well known Java frameworks/libraries.
The CloudEvents SDK for Java is composed by several modules, each one providing a different feature from the different sub specs of [CloudEvents specification](#supported-specification-features):
## Supported features
* [`cloudevents-api`] Module providing the `CloudEvent` and other base interfaces
* [`cloudevents-core`] Module providing `CloudEvent` implementation, `CloudEventBuilder` to create `CloudEvent`s programmatically, `EventFormat` to implement [Event Formats](https://github.com/cloudevents/spec/blob/v1.0/spec.md#event-format), `Message`/`MessageVisitor` to implement [Protocol bindings](https://github.com/cloudevents/spec/blob/v1.0/spec.md#protocol-binding)
* [`cloudevents-json-jackson`] Implementation of [JSON Event format] with [Jackson](https://github.com/FasterXML/jackson)
* [`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/)
* [`cloudevents-kafka`] Implementation of [Kafka Protocol Binding]
| | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/tree/v1.0) |
| :------------------------------------------------: | :---------------------------------------------------: | :---------------------------------------------------: |
| CloudEvents Core | :heavy_check_mark: | :heavy_check_mark: |
| AMQP Protocol Binding | :x: | :x: |
| - [Proton](amqp-proton.md) | :heavy_check_mark: | :heavy_check_mark: |
| AVRO Event Format | :x: | :x: |
| HTTP Protocol Binding | :heavy_check_mark: | :heavy_check_mark: |
| - [Vert.x](http-vertx.md) | :heavy_check_mark: | :heavy_check_mark: |
| - [Jakarta Restful WS](http-jakarta-restful-ws.md) | :heavy_check_mark: | :heavy_check_mark: |
| - [Basic](http-basic.md) | :heavy_check_mark: | :heavy_check_mark: |
| - [Spring](spring.md) | :heavy_check_mark: | :heavy_check_mark: |
| - [http4k][http4k]<sup></sup> | :heavy_check_mark: | :heavy_check_mark: |
| JSON Event Format | :heavy_check_mark: | :heavy_check_mark: |
| - [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: |
| Web hook | :x: | :x: |
You can look at the latest published artifacts on [Maven Central](https://search.maven.org/search?q=g:io.cloudevents).
<sub>† Source/artifacts hosted externally</sub>
## Get Started
You can start creating events using the `CloudEventBuilder`:
In order to start learning how to create, access and manipulate `CloudEvent`s,
check out the [Core module documentation](core.md).
```java
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import java.net.URI;
If you want to serialize and deserialize events and data back and forth to JSON,
check out the [Jackson Json module documentation](json-jackson.md).
final CloudEvent event = CloudEventBuilder.v1()
.withId("000")
.withType("example.demo")
.withSource(URI.create("http://example.com"))
.withData("application/json", "{}".getBytes())
.build();
```
Depending on the protocol and framework you're using, if you want to send and
receive CloudEvents, check out the dedicated pages:
Look at [`cloudevents-core`] README for more information.
- [AMQP using Proton](amqp-proton.md)
- [HTTP using Vert.x](http-vertx.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)
## Use an event format
If you're interested in implementing an object conforming to the `CloudEvent`
and related interfaces, in order to interoperate with the other components of
the SDK, check out the [API module documentation](api.md).
Event formats implementations are auto-discovered through an SPI. You can use them accessing through `EventFormatProvider`.
For example, given you have in your classpath the [`cloudevents-json-jackson`] module, you can serialize/deserialize an event to/from JSON using:
You can also check out the
[**Examples**](https://github.com/cloudevents/sdk-java/tree/main/examples).
```java
import io.cloudevents.core.format.EventFormatProvider;
import io.cloudevents.jackson.JsonFormat;
## Modules
EventFormat format = EventFormatProvider
.getInstance()
.resolveFormat(JsonFormat.CONTENT_TYPE);
The CloudEvents SDK for Java is composed by several modules, each one providing
a different feature from the different sub specs of
[CloudEvents specification](#supported-features):
// Serialize event
byte[] serialized = format.serialize(event);
- [`cloudevents-api`] Module providing the `CloudEvent` and other base
interfaces
- [`cloudevents-core`] Module providing `CloudEvent` implementation,
`CloudEventBuilder` to create `CloudEvent`s programmatically, `EventFormat`
to implement
[Event Formats](https://github.com/cloudevents/spec/blob/v1.0/spec.md#event-format),
`MessageReader` /`MessageWriter` to implement
[Protocol bindings](https://github.com/cloudevents/spec/blob/v1.0/spec.md#protocol-binding)
- [`cloudevents-bom`] Module providing a
[bill of materials (BOM)](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#bill-of-materials-bom-poms)
for easier integration of CloudEvents in other projects
- [`cloudevents-json-jackson`] Implementation of [JSON Event format] with
[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 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]
- [`cloudevents-amqp-proton`] Implementation of [AMQP Protocol Binding] with
[Proton](http://qpid.apache.org/proton/)
- [`cloudevents-spring`] Integration of `CloudEvent` with different Spring
APIs, like MVC, WebFlux and Messaging
// Deserialize event
CloudEvent event = format.deserialize(bytes);
```
You can look at the latest published artifacts on
[Maven Central](https://search.maven.org/search?q=g:io.cloudevents).
## Use a protocol binding
Each protocol binding has its own APIs, depending on the library/framework it's integrating with.
Check out the documentation of the protocol binding modules:
* [`cloudevents-http-vertx`]
* [`cloudevents-http-restful-ws`]
* [`cloudevents-kafka`]
[JSON Event Format]: https://github.com/cloudevents/spec/blob/v1.0/json-format.md
[JSON Event format]: https://github.com/cloudevents/spec/blob/v1.0/json-format.md
[Protobuf Event format]: https://github.com/cloudevents/spec/blob/v1.0.1/protobuf-format.md
[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
[`cloudevents-api`]: https://github.com/cloudevents/sdk-java/tree/master/api
[`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-http-vertx`]: https://github.com/cloudevents/sdk-java/tree/master/http/vertx
[`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
[AMQP Protocol Binding]: https://github.com/cloudevents/spec/blob/v1.0/amqp-protocol-binding.md
[`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/

67
docs/json-jackson.md Normal file
View File

@ -0,0 +1,67 @@
---
title: CloudEvents Json Jackson
nav_order: 4
---
# CloudEvents Json Jackson
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-json-jackson.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-json-jackson)
This module provides the JSON `EventFormat` implementation using Jackson and a
`PojoCloudEventDataMapper` to convert `CloudEventData` to POJOs using the
Jackson `ObjectMapper`.
For Maven based projects, use the following dependency:
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-json-jackson</artifactId>
<version>4.0.1</version>
</dependency>
```
## Using the JSON 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.vertx")
.withSource(URI.create("http://localhost"))
.build();
byte[]serialized = EventFormatProvider
.getInstance()
.resolveFormat(ContentType.JSON)
.serialize(event);
```
The `EventFormatProvider` will resolve automatically the `JsonFormat` using the
`ServiceLoader` APIs.
## Mapping `CloudEventData` to POJOs using Jackson `ObjectMapper`
Using the Jackson `ObjectMapper`, you can easily extract a POJO starting from
any `CloudEventData`:
```java
import io.cloudevents.core.data.PojoCloudEventData;
import io.cloudevents.jackson.PojoCloudEventDataMapper;
import static io.cloudevents.core.CloudEventUtils.mapData;
PojoCloudEventData<User> cloudEventData = mapData(
inputEvent,
PojoCloudEventDataMapper.from(objectMapper,User.class)
);
// check if cloudEventData is null
User user = cloudEventData.getValue();
```

122
docs/kafka.md Normal file
View File

@ -0,0 +1,122 @@
---
title: CloudEvents Kafka
nav_order: 5
---
# CloudEvents Kafka
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-kafka.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-kafka)
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/main/kafka-protocol-binding.md):
```xml
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-kafka</artifactId>
<version>4.0.1</version>
</dependency>
```
### Producing CloudEvents
To produce CloudEvents in Kafka, configure the KafkaProducer to use the provided
`CloudEventSerializer`:
```java
import java.util.Properties;
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import io.cloudevents.kafka.CloudEventSerializer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
public class CloudEventProducer {
public static void main(String[] args) {
Properties props = new Properties();
// Other config props
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, CloudEventSerializer.class);
try (KafkaProducer<String, CloudEvent> producer = new KafkaProducer<>(props)) {
// Build an event
CloudEvent event = CloudEventBuilder.v1()
.withId("hello")
.withType("example.kafka")
.withSource(URI.create("http://localhost"))
.build();
// Produce the event
producer.send(new ProducerRecord<>("your.topic", event));
}
}
}
```
You can configure the Encoding and EventFormat to use to emit the event.
Check out the
[`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
If you want your producer to use the `partitionkey` extension, you can use the
PartitionKeyExtensionInterceptor.
```java
producerProps.put(
ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,
io.cloudevents.kafka.PartitionKeyExtensionInterceptor.class
);
```
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/main/kafka/src/main/java/io/cloudevents/kafka/PartitionKeyExtensionInterceptor.java)
javadoc for more info.
## Consuming CloudEvents
To consume CloudEvents in Kafka, configure the KafkaConsumer to use the provided
`CloudEventDeserializer`:
```java
import java.time.Duration;
import java.util.Properties;
import io.cloudevents.CloudEvent;
import io.cloudevents.kafka.CloudEventDeserializer;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
public class CloudEventConsumer {
public static void main(String[] args) {
Properties props = new Properties();
// Other config props
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, CloudEventDeserializer.class);
try (KafkaConsumer<String, CloudEvent> consumer = new KafkaConsumer<>(props)) {
ConsumerRecords<String, CloudEvent> records = consumer.poll(Duration.ofSeconds(10));
records.forEach(rec -> {
System.out.println(rec.value().toString());
});
}
}
}
```

96
docs/protobuf.md Normal file
View File

@ -0,0 +1,96 @@
---
title: CloudEvents Protocol Buffers
nav_order: 4
---
# CloudEvents Protocol Buffers
[![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-protobuf.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-protobuf)
This module provides the Protocol Buffer (protobuf) `EventFormat` implementation using the Java
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>4.0.1</version>
</dependency>
```
No further configuration is required is use the module.
## Using the Protobuf Event Format
### Event serialization
```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.vertx")
.withSource(URI.create("http://localhost"))
.build();
byte[]serialized = EventFormatProvider
.getInstance()
.resolveFormat(ContentType.PROTO)
.serialize(event);
```
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();
...
}
```

Some files were not shown because too many files have changed in this diff Show More