Compare commits
326 Commits
Author | SHA1 | Date |
---|---|---|
|
0d96691f6d | |
|
c8a22b5175 | |
|
57f9867a5c | |
|
43f0d5b138 | |
|
efe7e01b75 | |
|
188c3c7085 | |
|
659bb8cc01 | |
|
9981635e7d | |
|
b54df5ca0c | |
|
a3decf768c | |
|
215e67f2c3 | |
|
514e00d75b | |
|
f7ac215bf1 | |
|
5f4abd144a | |
|
8d59d29bb1 | |
|
6ab42de007 | |
|
2e87988d0c | |
|
6d958b69d4 | |
|
1de03ad38a | |
|
4c5d0efb89 | |
|
01a9111d8b | |
|
e056d1b2d8 | |
|
efdf0ba866 | |
|
bd90d903ce | |
|
52a98d778c | |
|
010627e784 | |
|
a7904823c3 | |
|
e5a35ac472 | |
|
4d204cef89 | |
|
43afb6ceaa | |
|
9c6e7fd11e | |
|
98deac1599 | |
|
05baf9be8a | |
|
3add823e00 | |
|
5a03173dde | |
|
9ee16fb48c | |
|
0db4446163 | |
|
111fb55cfd | |
|
55fddb35fc | |
|
7b9d020acc | |
|
fb11b94f2b | |
|
eaef3becdd | |
|
b30324e916 | |
|
1f9fa13231 | |
|
a135755ec6 | |
|
677a2c2628 | |
|
826e099fc0 | |
|
76366338fc | |
|
4ef304115a | |
|
582feed520 | |
|
5ef1088a19 | |
|
698cdf7ad4 | |
|
4ebeab0e0f | |
|
4c81f3eacc | |
|
569e025cf0 | |
|
3614a4f5f4 | |
|
d64aff7327 | |
|
d1cff75230 | |
|
1591cb337a | |
|
f71303b7b7 | |
|
d59b33307a | |
|
e0d1961f35 | |
|
7c6b52ab30 | |
|
433ec5b274 | |
|
40fe91a5e0 | |
|
a43f90f4e2 | |
|
e488269510 | |
|
7d50c7fc7a | |
|
f1a86af656 | |
|
354f7a16ef | |
|
9132a13d81 | |
|
a491c85eb2 | |
|
6362bfbcd6 | |
|
f08a099ed9 | |
|
4139fb7e57 | |
|
adde53c817 | |
|
0dc10251ff | |
|
b9eaa2fcaa | |
|
f8d27b08bf | |
|
9125136530 | |
|
f35e6e610a | |
|
45ec85f8c1 | |
|
1d87fb7191 | |
|
8f9b741306 | |
|
2dd8ba95dd | |
|
1c29726e8c | |
|
6681205733 | |
|
a4bc7a8368 | |
|
4784f03e8c | |
|
ace6859ae0 | |
|
cc786251d5 | |
|
ceb06757a3 | |
|
8d91cdaee6 | |
|
d00ad967c0 | |
|
9231e6d230 | |
|
a94bc5c81c | |
|
32adfe9123 | |
|
0277ee4ae4 | |
|
202849307c | |
|
624ac693d8 | |
|
5a323942d3 | |
|
1587708805 | |
|
06c4ec5385 | |
|
73a3c370d5 | |
|
722f5205b3 | |
|
8ad857d8c7 | |
|
baa9b5927a | |
|
c41a2c3ba7 | |
|
3651cdae18 | |
|
ee4c85b1a1 | |
|
a0b0835180 | |
|
687e03bac5 | |
|
208b18c299 | |
|
9d45943844 | |
|
0a1a03db64 | |
|
7f355d10c1 | |
|
3234e30e55 | |
|
c8f10e9215 | |
|
ba9ccad5d2 | |
|
78355bb225 | |
|
2730ae4a13 | |
|
30fd6769eb | |
|
ff07dd8315 | |
|
928ebcfd6f | |
|
a4613c00d2 | |
|
69f0e20549 | |
|
e2b13109e4 | |
|
5e3bfc890f | |
|
13f8b56618 | |
|
a419d8bba3 | |
|
47bed5616d | |
|
baba37ccfd | |
|
32bcdcd3b9 | |
|
8b382734d9 | |
|
f05418cba9 | |
|
23cd08fcfd | |
|
70782da2c2 | |
|
48fc69e058 | |
|
e7e6e46bd5 | |
|
e523bfbfbf | |
|
12eee4da6e | |
|
d49ff9f69d | |
|
611f2292a7 | |
|
296230719b | |
|
d9592d5201 | |
|
00cdf9cb42 | |
|
bd11010138 | |
|
3a22557b83 | |
|
87c6915d9a | |
|
a7f87cf6cb | |
|
711277eacb | |
|
58570cf4d9 | |
|
24d108fe5d | |
|
f5d9b47c1c | |
|
59643c3368 | |
|
34483025df | |
|
e9d15daf28 | |
|
b89f45265b | |
|
a14f5eabec | |
|
5099b31f6c | |
|
bcc1434a39 | |
|
c1c55ac1d6 | |
|
5ca2c2de91 | |
|
2524cdf324 | |
|
eeb83c3567 | |
|
5a926820b4 | |
|
7c0b1e3c49 | |
|
377850cb61 | |
|
d2a89a4e08 | |
|
de786322aa | |
|
5037a69a80 | |
|
ceb5a2eeb2 | |
|
78a023d08c | |
|
bbce65aa64 | |
|
cc0892a440 | |
|
2d68c4843f | |
|
7f65c92dec | |
|
c9b3fa4b65 | |
|
394347db07 | |
|
6c78428513 | |
|
bf60a25098 | |
|
baf3b56d6d | |
|
8ac23dc479 | |
|
84fbb0c801 | |
|
20ebdbf87f | |
|
2411fe3508 | |
|
554198d9e0 | |
|
c1ff628511 | |
|
62fe155604 | |
|
34408236db | |
|
7696ffe4ec | |
|
b1aa399b63 | |
|
42a732623b | |
|
db745fd309 | |
|
c7baada605 | |
|
3bd9a6922f | |
|
118fc82f2b | |
|
d09b621409 | |
|
b3cdfc1ded | |
|
f52356e93a | |
|
de2052c4d6 | |
|
5e747e7278 | |
|
f9e31efaa3 | |
|
a09b03bd2f | |
|
f219b6937c | |
|
3cea0245b3 | |
|
7abf0c82ed | |
|
bb33df53d3 | |
|
b30c1dc2ec | |
|
e481d6feaf | |
|
36c6a78455 | |
|
8d3e86e672 | |
|
3d86cdd8f4 | |
|
2982f07f55 | |
|
1f58088f3c | |
|
3841e74e1e | |
|
dda867a6ea | |
|
e9c8da021a | |
|
53c0a84f43 | |
|
0a4caad417 | |
|
8a14cf4182 | |
|
6eecf29cf7 | |
|
29c9eaa23f | |
|
c3904fbff4 | |
|
69e605e2ef | |
|
b54a46fd07 | |
|
08aed3bc40 | |
|
f20208aec7 | |
|
c61f87f4d5 | |
|
0ca907be49 | |
|
8b4e586b64 | |
|
75171b9705 | |
|
700661b664 | |
|
6a982d83fa | |
|
b572bd88cf | |
|
39b9fc159c | |
|
4c98031c3b | |
|
0ce5f9535d | |
|
58a6426fe8 | |
|
d4ee5a6930 | |
|
e71cc5358c | |
|
862e4a657a | |
|
f42020333a | |
|
63ffe3be14 | |
|
900decaed0 | |
|
bd50e6f284 | |
|
a012e1e434 | |
|
fd1e6b2728 | |
|
12cca34d21 | |
|
83d9045897 | |
|
98a6b87d5b | |
|
c632f56f8b | |
|
7d85d19f1a | |
|
fd1c3268d9 | |
|
1bebbbbb68 | |
|
14dc3e5b8a | |
|
5c32b566e7 | |
|
28dcc2a009 | |
|
c386423787 | |
|
815c9cff26 | |
|
57bb81cc5c | |
|
1d07d9c392 | |
|
1ebf662b9b | |
|
6d057aa64d | |
|
34cd81455b | |
|
73d90d11bb | |
|
c4f2902dd6 | |
|
0fdfcc378d | |
|
c616a71947 | |
|
fb10bd3da5 | |
|
2ff79c06b6 | |
|
199f8c02e0 | |
|
cd5777791c | |
|
a485edffdb | |
|
9c167a67bb | |
|
30ca27afd8 | |
|
5586404278 | |
|
ba2db45f38 | |
|
5c28b36967 | |
|
d5f87a6df1 | |
|
bf3514459b | |
|
1b03ee1a93 | |
|
7d80d7263b | |
|
2b5e72c50a | |
|
4e38fc805d | |
|
f50f28ef3d | |
|
924dbed6e7 | |
|
cd913f368f | |
|
8d7c785afd | |
|
d207ecc311 | |
|
92c205a941 | |
|
d420efcda7 | |
|
ae4028d59d | |
|
4df01cd279 | |
|
7dcfdba30d | |
|
30c5fcf1e4 | |
|
0f94976f7b | |
|
41f48a5c84 | |
|
814bf80745 | |
|
3eecb4d7a9 | |
|
c7b9f3dab5 | |
|
8f1b8d2da9 | |
|
223d786a3f | |
|
d54dcf7f83 | |
|
ad5ab5d491 | |
|
59d4a79ed6 | |
|
ce98e7cbda | |
|
0113207a45 | |
|
670d1c13f4 | |
|
242b58a530 | |
|
5362a80afe | |
|
5096753658 | |
|
361a34cc63 | |
|
c35b313d15 | |
|
200aa6cd02 | |
|
dd07320779 | |
|
d25aaf8f49 | |
|
a79f8f9431 | |
|
833db5bd92 | |
|
e39291f655 | |
|
ae6dff25aa | |
|
dfa4d2c30e | |
|
2f0e22c560 | |
|
a5f88c3408 | |
|
d183489f04 | |
|
d0178c0bea |
|
@ -0,0 +1,14 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[{*.yaml, *.yml}]
|
||||
indent_size = 2
|
||||
|
||||
[*.java]
|
||||
max_line_length = 160
|
|
@ -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"
|
|
@ -0,0 +1,48 @@
|
|||
name: Test and Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- '[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Java ${{ matrix.java }} Test
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [ 8, 11, 17 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- run: |
|
||||
./mvnw clean install -DskipTests -B
|
||||
./mvnw verify -B
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
needs: test
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
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: ./mvnw clean deploy -Drelease -DskipTests -B
|
||||
env:
|
||||
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
|
||||
MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
|
||||
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
name: Bump version
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
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:
|
||||
name: Bump Java version
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEW_VERSION: ${{ github.event.inputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
- name: Bump version using Maven
|
||||
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" {} +'
|
||||
- name: Create version bump PR
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
title: "[Release] Bump to ${{ github.event.inputs.version }}"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "Bump to ${{ github.event.inputs.version }}"
|
||||
signoff: true
|
||||
branch: "bump/${{ github.event.inputs.version }}"
|
||||
body: >
|
||||
This PR performs the bump of the SDK to ${{ github.event.inputs.version }}.
|
||||
This PR is auto-generated by
|
||||
[create-pull-request](https://github.com/peter-evans/create-pull-request).
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
name: Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- '[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [ 8, 11, 17 ]
|
||||
name: Java ${{ matrix.java }} Test
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
distribution: 'temurin'
|
||||
- run: |
|
||||
./mvnw clean install -DskipTests -B
|
||||
./mvnw verify -B
|
||||
./mvnw javadoc:javadoc
|
|
@ -13,6 +13,8 @@ release.properties
|
|||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
.vscode/
|
||||
.attach_pid*
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
@ -34,3 +36,14 @@ release.properties
|
|||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
# Jekyll stuff
|
||||
_site/
|
||||
.sass-cache/
|
||||
.jekyll-cache/
|
||||
.jekyll-metadata
|
||||
|
||||
# MacOS
|
||||
*.DS_Store
|
||||
/http/restful-ws-jakarta/src/main/*
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright 2007-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
private static final String WRAPPER_VERSION = "0.5.6";
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
|
||||
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
* use instead of the default one.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||
".mvn/wrapper/maven-wrapper.properties";
|
||||
|
||||
/**
|
||||
* Path where the maven-wrapper.jar will be saved to.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||
".mvn/wrapper/maven-wrapper.jar";
|
||||
|
||||
/**
|
||||
* Name of the property which should be used to override the default download url for the wrapper.
|
||||
*/
|
||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("- Downloader started");
|
||||
File baseDirectory = new File(args[0]);
|
||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||
|
||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||
// wrapperUrl parameter.
|
||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||
String url = DEFAULT_DOWNLOAD_URL;
|
||||
if(mavenWrapperPropertyFile.exists()) {
|
||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||
try {
|
||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||
Properties mavenWrapperProperties = new Properties();
|
||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||
} catch (IOException e) {
|
||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||
} finally {
|
||||
try {
|
||||
if(mavenWrapperPropertyFileInputStream != null) {
|
||||
mavenWrapperPropertyFileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore ...
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if(!outputFile.getParentFile().exists()) {
|
||||
if(!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
try {
|
||||
downloadFileFromURL(url, outputFile);
|
||||
System.out.println("Done");
|
||||
System.exit(0);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("- Error downloading");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
||||
String username = System.getenv("MVNW_USERNAME");
|
||||
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
|
@ -1,15 +0,0 @@
|
|||
openssl aes-256-cbc -K $encrypted_3210c925a91b_key \
|
||||
-iv $encrypted_3210c925a91b_iv \
|
||||
-in .travis.secring.enc -out .travis.secring -d
|
||||
|
||||
gpg2 --import .travis.pubring
|
||||
|
||||
gpg2 --batch --allow-secret-key-import --import .travis.secring
|
||||
|
||||
export GPG_TTY=$(tty)
|
||||
|
||||
mvn clean deploy -P release -DskipTests \
|
||||
--settings .travis.settings.xml \
|
||||
-Dgpg.executable=gpg2 \
|
||||
-Dgpg.keyname=4F144A60ECA44F0D \
|
||||
-Dgpg.passphrase=$PASSPHRASE
|
|
@ -1,41 +0,0 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQGNBF2OZtgBDAC7V7wK9PBKvsPw1EMFpKyP0PAVWj1Y7V4MEWJ8zCYXLawpdYnc
|
||||
M8a5AizkAHXP6rx/fgZ+WKDbZUeQ0pyz+b7MjV7gYAsU8UZ3q1aMk5mi31lao/uw
|
||||
JRglmC5PO4QYDRrpc0fRTPxuzMzghAUfvOVfa4BZFV4K39sBgU+g9fBTlJ8e1xoW
|
||||
EoIsKR1DOvcZPU935LC0gq+auie4h+riIuiDLl2b0yiZhgJNUHhLhKaRhBERTgos
|
||||
Mkstd/bCNHBUcs0tsIzg9pTgrzvncvnwstSQR18H9Jw6tVjJQoF4HUXtJApwku3T
|
||||
lA9O3HRQZUyL7PyKvQ2UxDiL3LWv4PqeiH/MILVpANNbDMUCGTP32ru5CA19rzHf
|
||||
TTJPpVavo4vDeVoYMYI3uwxfOek/ZHmDd1ECSDBKQfE7Hwj56hpx+zez6j5G/Zzb
|
||||
4ohuwv9iu5+NWvV7prIh9GLW3esdwJPBrTDKPrLZFNc/DwrHfrc9Z2JjIbDmSJAB
|
||||
k2gujpLExBi9KlMAEQEAAbQfZmFiaW9qb3NlIDxmYWJpb2pvc2VAZ21haWwuY29t
|
||||
PokB1AQTAQgAPhYhBB80EFbYrr5VW0Ggik8USmDspE8NBQJdjmbYAhsDBQkDwmcA
|
||||
BQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEE8USmDspE8NDAUMALgvLCtkrtRe
|
||||
egNqDopgl7UoZjrfDoKojMr8l6kxIBWb/HzYBAOKSVquhdn71iRySuUSYL2BeK8f
|
||||
As144J9XkAo4cdNXvGTorOpjt2qRMdJzBONE8eVCRrW50uH7pq4YkB+8qVRzXNXz
|
||||
PGDWN0BQrJOMh12BP0JVWomABUxQsAE6NbRqu5gRcbEsLsp87AmLlVw27/J3wT/q
|
||||
Dg/LePNtBazp/LwV+j6/IkPmF08w//xQXZoKoNX7VHJ+4uIUXR8iL0SiN/2ceghu
|
||||
3r9hAUAnJf6Qewe+xlxHDCEueVR8RUvYjYbEwkRHGJlkxWyraiV+CQlyP6Vswr3N
|
||||
f87NPJTKv40pcndKiuajuNfb+6bRuOG4QMn0/GfpVrdui1iIKSVEAWIkJ+pSAlJh
|
||||
++SqqGW25M5zoXQPVfa51pzaiDa2BY9BXwrH6/jg/2GeCnCRSf53Xx/HZev/Uoro
|
||||
hCpOY2uLz+NbnprF6p9VcxhTjrl/h1ObPOosZWDCsi0BskhvLXwuPLkBjQRdjmbY
|
||||
AQwAudhK/rcocFWNDtfsPSH6zXfBEGLTHkf2hgKbVMVdNMVqxRSTieQZHbLHlA2E
|
||||
2w+ssYd9d5yQrkj+D53e9apQQretJir4X3CvTlEQ/KfWWkRH1eUNJF2FFUcvt3LR
|
||||
jRcbIy4UeJnI/boKxdHNXhuPjdx5ljPkXY7tkYHZt5jih6oiC3m4GbdsAHHAq68p
|
||||
R1IkpQnQUfYBwBzVINahStm5UcJFSXX1ZUsj8OYJOxxuObaGyiVydGOtSp7/et60
|
||||
JEi/F3Zgx//x4fNFjUTNG/2gzakM9pY24p8kS2b4o7mrNdfg6vLdMLZHjiEn4+tZ
|
||||
iI/4ve2ZKd4jLJOB20l79rpE78HrI3IJ6P5H2bRpxPNSWOVkNqcPhjpyYQLl0wU+
|
||||
zngZ8fcMvIASFWa96yG1yhr7Ly5KuTf+Ka5vOInXvZlhWe46CFr5r7waawtIrOkF
|
||||
BiXt0MIo6++4ZJdhHRpXAwctRQYj+ky7RGMGiI5GjzO1JI7IUgL4kpXidAGs4fj9
|
||||
anppABEBAAGJAbwEGAEIACYWIQQfNBBW2K6+VVtBoIpPFEpg7KRPDQUCXY5m2AIb
|
||||
DAUJA8JnAAAKCRBPFEpg7KRPDbM8DACYAP9GjOj4vazpgVX2jlQr/vbxSMp/4Qmz
|
||||
yrlTKDGfvNMot+j1NIom+CyMhYLonig90wXhacch6Ocu1RRSd/GmVyj65sTnilPr
|
||||
VSO+KmHbKDk+YBhBXoCm4pQT31/Jaw/vqz3+ZlmftFXXO96VE/IVMM8KOxmLHAao
|
||||
nmqaOOL35Uv0waUPlFjSqiGxuzPlDvBsDxpNgKQ9qUEWKKFInOAeuDJ7uONdNgOZ
|
||||
BHEMDLveLS+rkLHetMUxIuY4bqozc8jAmDOJnuTEhXUXoWLRHtgiVO3tYEYMvdRi
|
||||
bu4Oh5uJ1pVDtAOLa6cEkuVFZSRFct3SJp2PX6zN1hov+EcXf1k2W0GAnVMyWHnC
|
||||
+As9BACop5JNaG/B5johOx1IqwhPn/4IownwvKyhicT0DlLTXCy5+MYHbJi9DdIs
|
||||
L1xsu5QcGCmnH2Ur9ml61Vfb4LmlHS6n0eZ5N6mpYREKKmzvNiD47u6C5j6tDq51
|
||||
Q8rGENjZbxXtht/6AqzvOoML/Hq3xNs=
|
||||
=dors
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
Binary file not shown.
|
@ -1,11 +0,0 @@
|
|||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<servers>
|
||||
<server>
|
||||
<id>ossrh</id>
|
||||
<username>${env.SONATYPE_USERNAME}</username>
|
||||
<password>${env.SONATYPE_PASSWORD}</password>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>
|
37
.travis.yml
37
.travis.yml
|
@ -1,37 +0,0 @@
|
|||
language: java
|
||||
sudo: false
|
||||
jdk:
|
||||
- openjdk8
|
||||
- openjdk9
|
||||
- openjdk10
|
||||
- openjdk11
|
||||
cache:
|
||||
directories:
|
||||
- ".autoconf"
|
||||
- "$HOME/.m2"
|
||||
env:
|
||||
global:
|
||||
- secure: P+bDfjq+RCVmZjkwqC2m1eV2jr2VbR3NA8o9BcJFDduwKHboflzp92sL/Risp9QGwdRomPIfzUBL8Dq13lgDSgflfkZpbLhRamXi06E74MOHIZeV3EYbhOTmWa7DbBueUCTVmxmfgNI+207EtlKkW9ZBsqky/tUuLXdFCjjonDN3f3PmRI7odxZ4r5di46h73yynh+8+pAx7T6K52PJNGXtAZflKU2NHnfAUainAHMOY3UAOfL0vSVhKvW7s6i9/qrqUAXgN0PUeiAwKi0RnuoTEAe8n+GILYH8xRkkkTLZj3uxwI9lTYdtN2wOCJZ8U7bmy8jQxFCaes/LgyfL5F4e4r3FUPrFpgPOBqiP+7T20aoCxbMFQ2EUkRbwXsNXEQu1FCBNH/sMdw7FEfOfE43jIe4yQIoWlmz+Su0RGqgwOiolMb0O10M31apKqKmmyPBb9c9AWwraKZ1FRe+tTVVRRBlWUAxYjLhha4uczW3oY+sE2yY8k5gPN5JNWqvy92UZUupfAKoxh21C2K5ygeQR7crEnwwtw1flu9QUWfFYBvUKK7aC33WwRq+4RKB3oPeD9b/+Ix5jUcad9wqy5rw0rKJRHJzvYoAaJ+kIlnD9nZLq52fj5gs/+1JOlRpE3rP0z9M6qUPE5QWHa8DGYvXwe8s93gZbcBPqxVNv1U6o=
|
||||
- secure: UFn72lygDZrU66+kJJ7tH2MiGvq2sfUu396TaVkZipVz7wW0NZ//wkI1w/SKluPjEa/dJ3hcxz+IR8R9QCcZgIbBhaxgmjCG10majm24+3TqcqeE/lUHtD00LGDJUmSbr2vsenHYRZk7TNe3Cb3YrKL2APSTVaMWJo3fCMT7BaydXuAL8D4ZAJrPTw9S9GsyjAGaPhN8P8wsJCBhRhOxS+TuMcjLTI6fXIN5kdZz6IygpRhmx7RSdICAl+otHz7znUQh4PyfAPGPItwACXTcDfF4lQ/DUNfA8FcRnR76lgvMSIS291YLy22ToBe5LT5smQWT4VE3TDVhkn9uAyTGXBN1NlOsiqKxW8eyFUckU0rQIkHHz6FFAtVnklt4EArN1l+KJksl524kBTph/IKWONiTNGKUwc4fIzB8o/iMIa0ZtadR0Fde32ssiEk7+lvkEHypZUe/WF+mEi8Y4PUeM9OBP/Gdxb7hLLzCaJNfZ3NRGYRZJieVk608jnRi7XADSRnw830ga5A3YbkZo7ljAD2UN3BKwhffs1FEnmGBz2mdnPie/HJ0nTf7g7/vIxgZT7UGcZ35olFah2kUEbiZkbT6OmKBY0a5regUB7+6aTjcP5aPFLkacz5BIs1jvrjcsalYEi+KFKWfmeLVQurqEQSpO0ZOVS7cAfm4qGTkv8Y=
|
||||
- secure: Fl7zB+DmZTER2j1PfHUlJORWCkqSWPViwqPeV+OvHK59Oisgv+MJ7w5jU+N5iEmKi7V6moDCpdnChAlLN4XxYNKWHhuw1jv1GAvr083qpTRikbHvzPGNGU1g3g5tResslYJRW+U8pbhl+i50I7i0+VbYshHDUvtKiC6Wfb4KSDkIGNPhekmw1RZ/tZkZPwgX95sAqioyeI/AGAlI7x209eIHL6igxt5auc4XsfqTU9fjIoyyp1jRP4WRJ3tatJshAiwM+9SI0vzZyIe7w3d6y9CtEGxcBlCcCYn18YJ1+7da3Bfxv4nrIfsRKuVW/p2cypc7N26WMgsVlUzXryX/Bqie4SvFBiEM+WJey8YW2wc8Fr0ndcFjPFIsvpo+uHQRkwzuGspbzmfIfOJVVKCbIhAuKQZSGuJB5bSKtQxk0Vgz35Q4ag8gr8UfrjjoMtP0bb4N7nzYTHB/osDDKT9gmM/lBWyjXPRtCHWDgPJxSWrLxoXVWnzGZC/6Jc0eeJQlVOZ3YUViqSV2x3cXlOjyzSCkoDGk7fMFgPL+YP1gvn47KMhN9MtrSY+vkGqwDWrXb+0Mq4z7EIog5ZD+qE5XB014lDnQEk7GVpp5DVktdMBXkCBxpmBPDnpPF2BuTgf7nAMtJcNgq0PpLYc2d4W9ZpB0rD5KO1G83/ai+KnCWig=
|
||||
jobs:
|
||||
include:
|
||||
- stage: maven central release
|
||||
jdk: openjdk8
|
||||
script: skip
|
||||
deploy:
|
||||
provider: script
|
||||
script: bash .travis.deploy.sh
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
- stage: github release
|
||||
jdk: openjdk8
|
||||
script: skip
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: RvyJMne8BdDQGwiVFb4j6YQz1AVKDSbJNepHDNUt21lQSm4TGmJ61MKZdHI+UsCTlLTVPmDLM3SvFL6ILSkDVuM67VCSDRwBp+mmPqouMzFWPQVpfVjfPOjSrr3TTZON2h39RyIifdO7laMQ1nv3HuLCciEjtH7nQ35fHBbs5TkgNnBoUoVIeWmRO667RlJAGvGfFLjwi/a96BtyR3gBGvlosHCX1Utx4Tp8t6SHTrLKLxSDUTiQp63fuygJVOmlzEUKv94VGmwacft2QUoommK4vFkDji5v18+4RwfaN1py/5riO7FJMVhmsNMrD5FJBgaaD8C0ufXGWzDWZPwTeiYxH9D2Vug3ySyzwvwt37wEy9PdQaNipOzZvvcCsamA3P+nYuqoAchCIUoxwi3TrAzd/0t3n/VP8PzMvSbu/BxQg3HRo7yZJmUVJgkyExc/s35f2gKzzUsfO9We6NJCYA4CmSe1ftblMOplr4wTI+U3xV9hvcawhTgY1mENUv3zFbte/2rwai9GpEPxMh2AScxQ0b2Vu3t2O25zcEHGoMKziZ5TU83mbvnpTu5ImUfw0JEbWmZiaNgTkTjwMI7LsQSXZsmtiu8C2u7ohepiy2UPXht7FFm6vMhgRTry3gyh2GURRWUDM8pgctn7uYScmd5br6vdNlMIm4e5huj7Dak=
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
87
CHANGELOG.md
87
CHANGELOG.md
|
@ -1,87 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.2.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Improved base64 marshalling: PR [#79](https://github.com/cloudevents/sdk-java/pull/79)
|
||||
|
||||
## [1.1.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Support for custom validator: PR [#73](https://github.com/cloudevents/sdk-java/pull/73)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix for the issue [#74](https://github.com/cloudevents/sdk-java/issues/74)
|
||||
- Fix for the issue [#67](https://github.com/cloudevents/sdk-java/issues/67)
|
||||
|
||||
## [1.0.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Support for [Spec v1.0](https://github.com/cloudevents/spec/tree/v1.0)
|
||||
- Improved readme to end request for vertx sample [PR 62](https://github.com/cloudevents/sdk-java/pull/62)
|
||||
- Allow providing an external validator [PR 65](https://github.com/cloudevents/sdk-java/pull/65)
|
||||
|
||||
## [0.3.1]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Vulnerable dependency `com.fasterxml.jackson.core:jackson-databind`
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
### Added
|
||||
- [Attributes](./api/src/main/java/io/cloudevents/Attributes.java) marker
|
||||
interface for context attributes
|
||||
- Support for [Spec v0.3](https://github.com/cloudevents/spec/tree/v0.3)
|
||||
- [ExtensionFormat](./api/src/main/java/io/cloudevents/ExtensionFormat.java)
|
||||
interface for extensions
|
||||
- [HTTP Marshallers](./api/src/main/java/io/cloudevents/v02/http/Marshallers.java) with bare minium marshallers for HTTP Transport Binding
|
||||
- [HTTP Unmarshallers](./api/src/main/java/io/cloudevents/v02/http/Unmarshallers.java) with bare minium unmarshallers for HTTP Transport Binding
|
||||
- [Kafka Marshallers](./kafka/src/main/java/io/cloudevents/v02/kafka/Marshallers.java) with bare minimum marshallers for Kafka Transport Binding
|
||||
- [CloudEventsKafkaProducer](./kafka/src/main/java//io/cloudevents/kafka/CloudEventsKafkaProducer.java) The CloudEvents producer that uses Kafka Clients API
|
||||
- [Kafka Unmarshallers](./kafka/src/main/java/io/cloudevents/v02/kafka/Unmarshallers.java) with bare minium unmarshallers for Kafka Transport Binding
|
||||
- [CloudEventsKafkaConsumer](./kafka/src/main/java//io/cloudevents/kafka/CloudEventsKafkaConsumer.java) The CloudEvents consumer that uses Kafka Clients API
|
||||
- [BinaryMarshaller](./api/src/main/java/io/cloudevents/format/BinaryMarshaller.java) To help in the case of you need to create your own marshallers for binary content mode
|
||||
- [BinaryUnmarshaller](./api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java) To help in the case of you need to create your own unmarshallers for binary content mode
|
||||
- [StructuredMarshaller](./api/src/main/java/io/cloudevents/format/StructuredMarshaller.java) To help in the case of you need to create your own marshallers for structured content mode
|
||||
- [StructuredUnmarshaller](./api/src/main/java/io/cloudevents/format/StructuredUnmarshaller.java) To help in the case of you need to create your own unmarshallers for structured content mode
|
||||
|
||||
|
||||
### Changed
|
||||
- CloudEvent interface signature, breaking the backward compatibility
|
||||
- Class: `io.cloudevents.v02.CloudEvent` moved to `io.cloudevents.v02.CloudEventImpl`
|
||||
- Class: `io.cloudevents.v02.CloudEventImpl` had changed its signature, breaking the
|
||||
backward compatibility
|
||||
- Method: `io.cloudevents.json.Json.fromInputStream` had moved to Generics signature
|
||||
- Class: `io.cloudevents.http.V02HttpTransportMappers` moved to
|
||||
`io.cloudevents.v02.http.BinaryAttributeMapperImpl` and had changed is signature breaking the backward compatibility
|
||||
|
||||
### Removed
|
||||
- Support for Spec v0.1
|
||||
- Class: `io.cloudevents.impl.DefaultCloudEventImpl`, in favor of a impl for each
|
||||
version
|
||||
- Class: `io.cloudevents.CloudEventBuilder`, in favor of a builder for each version
|
||||
- Enum: `io.cloudevents.SpecVersion`, in favor of specialization of specs
|
||||
- Method: `io.cloudevents.json.Json.decodeCloudEvent`
|
||||
- Class: `io.cloudevents.http.V01HttpTransportMappers` due the unsupported v0.1
|
||||
- interface: `io.cloudevents.http.HttpTransportAttributes`, in favor of the new
|
||||
abstract envelope signature
|
||||
- interface: `io.cloudevents.Extension` in favor of
|
||||
`io.cloudevents.extensions.ExtensionFormat`
|
||||
|
||||
[Unreleased]: https://github.com/cloudevents/sdk-java/compare/v1.2.0...HEAD
|
||||
[1.2.0]: https://github.com/cloudevents/sdk-java/compare/v1.1.0...v1.2.0
|
||||
[1.1.0]: https://github.com/cloudevents/sdk-java/compare/v1.0.0...v1.1.0
|
||||
[1.0.0]: https://github.com/cloudevents/sdk-java/compare/v0.3.1...v1.0.0
|
||||
[0.3.1]: https://github.com/cloudevents/sdk-java/compare/v0.3.0...v0.3.1
|
||||
[0.3.0]: https://github.com/cloudevents/sdk-java/compare/v0.2.1...v0.3.0
|
|
@ -0,0 +1,167 @@
|
|||
# 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
|
||||
managing your work and a positive, satisfying experience when contributing
|
||||
your code. Thanks for getting involved! :rocket:
|
||||
|
||||
* [Getting Started](#getting-started)
|
||||
* [Branches](#branches)
|
||||
* [Commit Messages](#commit-messages)
|
||||
* [Staying current with main](#staying-current-with-main)
|
||||
* [Submitting and Updating a Pull Request](#submitting-and-updating-a-pull-request)
|
||||
* [Congratulations!](#congratulations)
|
||||
|
||||
## Getting Started
|
||||
|
||||
When creating a pull request, first fork this repository and clone it to your
|
||||
local development environment. Then add this repository as the upstream.
|
||||
|
||||
```console
|
||||
git clone https://github.com/mygithuborg/sdk-java.git
|
||||
cd sdk-java
|
||||
git remote add upstream https://github.com/cloudevents/sdk-java.git
|
||||
```
|
||||
|
||||
## Branches
|
||||
|
||||
The first thing you'll need to do is create a branch for your work.
|
||||
If you are submitting a pull request that fixes or relates to an existing
|
||||
GitHub issue, you can use the issue number in your branch name to keep things
|
||||
organized.
|
||||
|
||||
```console
|
||||
git fetch upstream
|
||||
git reset --hard upstream/main
|
||||
git checkout FETCH_HEAD
|
||||
git checkout -b 48-fix-http-agent-error
|
||||
```
|
||||
|
||||
## Commit Messages
|
||||
|
||||
Please follow the
|
||||
[Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/#summary).
|
||||
The first line of your commit should be prefixed with a type, be a single
|
||||
sentence with no period, and succinctly indicate what this commit changes.
|
||||
|
||||
All commit message lines should be kept to fewer than 80 characters if possible.
|
||||
|
||||
An example of a good commit message.
|
||||
|
||||
```log
|
||||
docs: remove 0.1, 0.2 spec support from README
|
||||
```
|
||||
|
||||
### Signing your commits
|
||||
|
||||
Each commit must be signed. Use the `--signoff` flag for your commits.
|
||||
|
||||
```console
|
||||
git commit --signoff
|
||||
```
|
||||
|
||||
This will add a line to every git commit message:
|
||||
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
||||
The sign-off is a signature line at the end of your commit message. Your
|
||||
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||
it on as open-source code. See [developercertificate.org](http://developercertificate.org/)
|
||||
for the full text of the certification.
|
||||
|
||||
Be sure to have your `user.name` and `user.email` set in your git config.
|
||||
If your git config information is set properly then viewing the `git log`
|
||||
information for your commit will look something like this:
|
||||
|
||||
```
|
||||
Author: Joe Smith <joe.smith@email.com>
|
||||
Date: Thu Feb 2 11:41:15 2018 -0800
|
||||
|
||||
Update README
|
||||
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
```
|
||||
|
||||
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 `main`
|
||||
|
||||
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/main
|
||||
```
|
||||
|
||||
This may cause conflicts if the files you are changing on your branch are
|
||||
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`.
|
||||
|
||||
|
||||
If you've already pushed some changes to your `origin` fork, you'll
|
||||
need to force push these changes.
|
||||
|
||||
```console
|
||||
git push -f origin 48-fix-http-agent-error
|
||||
```
|
||||
|
||||
## Submitting and Updating Your Pull Request
|
||||
|
||||
Before submitting a pull request, you should make sure that all of the tests
|
||||
successfully pass.
|
||||
|
||||
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/main
|
||||
# fix any potential conflicts
|
||||
git push -f origin 48-fix-http-agent-error
|
||||
```
|
||||
|
||||
This will cause the pull request to be updated with your changes, and
|
||||
CI will rerun.
|
||||
|
||||
A maintainer may ask you to make changes to your pull request. Sometimes these
|
||||
changes are minor and shouldn't appear in the commit log. For example, you may
|
||||
have a typo in one of your code comments that should be fixed before merge.
|
||||
You can prevent this from adding noise to the commit log with an interactive
|
||||
rebase. See the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History)
|
||||
for details.
|
||||
|
||||
```console
|
||||
git commit -m "fixup: fix typo"
|
||||
git rebase -i upstream/main # follow git instructions
|
||||
```
|
||||
|
||||
Once you have rebased your commits, you can force push to your fork as before.
|
||||
|
||||
## Congratulations!
|
||||
|
||||
Congratulations! You've done it! We really appreciate the time and energy
|
||||
you've given to the project. Thank you.
|
2
LICENSE
2
LICENSE
|
@ -186,7 +186,7 @@
|
|||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Copyright 2018-Present The CloudEvents Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# Maintainers
|
||||
|
||||
Current active maintainers of this SDK:
|
||||
|
||||
- [Pierangelo Di Pilato](https://github.com/pierDipi)
|
|
@ -0,0 +1,39 @@
|
|||
# Maintainer's Guide
|
||||
|
||||
## Tips
|
||||
|
||||
Here are a few tips for repository maintainers.
|
||||
|
||||
* Stay on top of your pull requests. PRs that languish for too long can become difficult to merge.
|
||||
* Work from your own fork. As you are making contributions to the project, you should be working from your own fork just as outside contributors do. This keeps the branches in github to a minimum and reduces unnecessary CI runs.
|
||||
* Try to proactively label issues and pull requests with labels
|
||||
* Actively review pull requests as they are submitted
|
||||
* Triage issues once in a while in order to keep the repository alive.
|
||||
* If some issues are stale for too long because they are no longer valid/relevant or because the discussion reached no significant action items to perform, close them and invite the users to reopen if they need it.
|
||||
* If some PRs are no longer valid due to conflicts, but the PR is still needed, ask the contributor to rebase their PR.
|
||||
* If some issues and PRs are still relevant, use labels to help organize tasks.
|
||||
* If you find an issue that you want to create a pull request for, be sure to assign it to yourself so that other maintainers don't start working on it at the same time.
|
||||
|
||||
## Landing Pull Requests
|
||||
|
||||
When landing pull requests, be sure to check the first line uses an appropriate commit message prefix (e.g. docs, feat, lib, etc). If there is more than one commit, try to squash into a single commit. Usually this can just be done with the GitHub UI when merging the PR. Use "Squash and merge". To help ensure that everyone in the community has an opportunity to review and comment on pull requests, it's often good to have some time after a pull request has been submitted, and before it has landed. Some guidelines here about approvals and timing.
|
||||
|
||||
* No pull request may land without passing all automated checks
|
||||
* All pull requests require at least one approval from a maintainer before landing
|
||||
* A pull request author may approve their own PR, but will need an additional approval to land it
|
||||
* If a maintainer has submitted a pull request and it has not received approval from at least one other maintainer, it can be landed after 72 hours
|
||||
* If a pull request has both approvals and requested changes, it can't be landed until those requested changes are resolved
|
||||
|
||||
## Branch Management
|
||||
|
||||
The `main` branch is the bleeding edge. New major versions of the module
|
||||
are cut from this branch and tagged. If you intend to submit a pull request
|
||||
you should use `main HEAD` as your starting point.
|
||||
|
||||
Each major release will result in a new branch and tag. For example, the
|
||||
release of version 1.0.0 of the module will result in a `v1.0.0` tag on the
|
||||
release commit, and a new branch `v1.x.y` for subsequent minor and patch
|
||||
level releases of that major version. However, development will continue
|
||||
apace on `main` for the next major version - e.g. 2.0.0. Version branches
|
||||
are only created for each major version. Minor and patch level releases
|
||||
are simply tagged.
|
170
README.md
170
README.md
|
@ -1,105 +1,123 @@
|
|||
# Java SDK for CloudEvents API
|
||||
|
||||
[](https://travis-ci.org/cloudevents/sdk-java)
|
||||
[](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/io.cloudevents/cloudevents-parent)
|
||||
[](http://www.javadoc.io/doc/io.cloudevents/cloudevents-api)
|
||||
[](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.
|
||||
|
||||
__Checkout the [changelog](./CHANGELOG.md)__
|
||||
Using the Java SDK you can:
|
||||
|
||||
## Motivation
|
||||
- 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 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 `0.3` 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).
|
||||
To check out the complete documentation and how to get started, look at the dedicated website
|
||||
https://cloudevents.github.io/sdk-java/.
|
||||
|
||||
## Java API
|
||||
## Status
|
||||
|
||||
For Maven based projects, use the following to configure the CloudEvents Java SDK:
|
||||
This SDK is considered **work in progress**. The community is working hard to
|
||||
bring you a new major version of the SDK with major enhancements both to APIs
|
||||
and to implementation.
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-api</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
```
|
||||
If you want to know more about v1 of this SDK, check out the
|
||||
[v1 readme](https://github.com/cloudevents/sdk-java/tree/1.x)
|
||||
|
||||
Application developers can now create strongly-typed CloudEvents, such as:
|
||||
Stay tuned!
|
||||
|
||||
```java
|
||||
import io.cloudevents.v1.CloudEventBuilder;
|
||||
import io.cloudevents.v1.CloudEventImpl;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
Supported features of the specification:
|
||||
|
||||
// given
|
||||
final String eventId = UUID.randomUUID().toString();
|
||||
final URI src = URI.create("/trigger");
|
||||
final String eventType = "My.Cloud.Event.Type";
|
||||
final MyCustomEvent payload = ...
|
||||
| | [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) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| AVRO Event Format | :x: | :x: |
|
||||
| HTTP Protocol Binding | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| - [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: |
|
||||
|
||||
// add trace extension usin the in-memory format
|
||||
final DistributedTracingExtension dt = new DistributedTracingExtension();
|
||||
dt.setTraceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01");
|
||||
dt.setTracestate("rojo=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01");
|
||||
<sub>† Source/artifacts hosted externally</sub>
|
||||
|
||||
final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt);
|
||||
## Documentation
|
||||
|
||||
// passing in the given attributes
|
||||
final CloudEventImpl<MyCustomEvent> cloudEvent =
|
||||
CloudEventBuilder.<MyCustomEvent>builder()
|
||||
.withType(eventType)
|
||||
.withId(eventId)
|
||||
.withSource(src)
|
||||
.withData(payload)
|
||||
.withExtension(tracing)
|
||||
.build();
|
||||
Documentation is available at https://cloudevents.github.io/sdk-java/.
|
||||
|
||||
// marshalling as json
|
||||
final String json = Json.encode(cloudEvent);
|
||||
```
|
||||
Javadocs are available on [javadoc.io](https://www.javadoc.io):
|
||||
|
||||
Or, an event with binary event data:
|
||||
- [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)
|
||||
|
||||
```java
|
||||
import io.cloudevents.v1.CloudEventBuilder;
|
||||
import io.cloudevents.v1.CloudEventImpl;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
You can check out the examples in the [examples](examples) directory.
|
||||
|
||||
// given
|
||||
final String eventId = UUID.randomUUID().toString();
|
||||
final URI src = URI.create("/trigger");
|
||||
final String eventType = "My.Cloud.Event.Type";
|
||||
final byte[] payload = "a-binary-event-data".getBytes();
|
||||
## Used By
|
||||
|
||||
// passing in the given attributes
|
||||
final CloudEventImpl<String> cloudEvent =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withType(eventType)
|
||||
.withId(eventId)
|
||||
.withSource(src)
|
||||
.withDataBase64(payload)
|
||||
.build();
|
||||
| [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> | |
|
||||
|
||||
// marshalling as json that will have the data_base64
|
||||
final String json = Json.encode(cloudEvent);
|
||||
```
|
||||
## Community
|
||||
|
||||
There are [other detailed ways](./api/README.md) of how to use the marshallers
|
||||
and unmarshallers with HTTP transport binding.
|
||||
- There are bi-weekly calls immediately following the
|
||||
[Serverless/CloudEvents call](https://github.com/cloudevents/spec#meeting-time)
|
||||
at 9am PT (US Pacific). Which means they will typically start at 10am PT,
|
||||
but if the other call ends early then the SDK call will start early as well.
|
||||
See the
|
||||
[CloudEvents meeting minutes](https://docs.google.com/document/d/1OVF68rpuPK5shIHILK9JOqlZBbfe91RNzQ7u_P7YCDE/edit#)
|
||||
to determine which week will have the call.
|
||||
- Slack: #cloudeventssdk channel under
|
||||
[CNCF's Slack workspace](https://slack.cncf.io/).
|
||||
- Email: https://lists.cncf.io/g/cncf-cloudevents-sdk
|
||||
- Contact for additional information: Francesco Guardiani (`@slinkydeveloper`
|
||||
on slack), Fabio José (`@fabiojose` on slack).
|
||||
|
||||
## Kafka
|
||||
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/main/docs)
|
||||
directory. In particular, in there you will find information concerning how SDK
|
||||
projects are
|
||||
[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/main/docs/GOVERNANCE.md#additional-information)
|
||||
information.
|
||||
|
||||
The support for kafka transport binding is available. Read the
|
||||
[documentation and examples](./kafka/README.md) of use.
|
||||
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).
|
||||
|
||||
## Possible Integrations
|
||||
## Additional SDK Resources
|
||||
|
||||
The API is kept simple, for allowing a wide range of possible integrations:
|
||||
- [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)
|
||||
|
||||
* [CDI](cdi/)
|
||||
* [Eclipse Vert.x](http/vertx/)
|
||||
[http4k]: https://www.http4k.org/guide/reference/cloud_events/
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# Release Process
|
||||
|
||||
The release process is automated with Github actions. In order to perform a release:
|
||||
|
||||
1. Check if main CI pass.
|
||||
1. Open the Github repository main page and go in the tab "Actions". Trigger the workflow "Bump version" and insert the new version to release. This will create a new release PR.
|
||||
1. Check the release PR, merge it and cleanup the created branch.
|
||||
1. Wait for the CI to complete the deploy of the modules to OSSRH.
|
||||
1. Using the Github UI, create a new release, specifying the release notes and the tag to use.
|
||||
1. Trigger again the workflow "Bump version" to bump versions back to a snapshot version.
|
||||
1. Check the snapshot release PR, merge it and cleanup the created branch.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# AMQP Protocol Binding
|
||||
|
||||
Javadoc: [](http://www.javadoc.io/doc/io.cloudevents/cloudevents-amqp-proton)
|
||||
|
||||
Documentation: https://cloudevents.github.io/sdk-java/amqp-proton
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cloudevents-amqp-proton</artifactId>
|
||||
<name>CloudEvents - Proton AMQP Binding</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<protonj.version>0.34.1</protonj.version>
|
||||
<jsr305.version>3.0.2</jsr305.version>
|
||||
<module-name>io.cloudevents.amqp.proton</module-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.qpid</groupId>
|
||||
<artifactId>proton-j</artifactId>
|
||||
<version>${protonj.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>${jsr305.version}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj-core.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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.amqp;
|
||||
|
||||
import io.cloudevents.SpecVersion;
|
||||
import io.cloudevents.amqp.impl.AmqpConstants;
|
||||
import io.cloudevents.amqp.impl.ProtonAmqpBinaryMessageReader;
|
||||
import io.cloudevents.amqp.impl.ProtonAmqpMessageWriter;
|
||||
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 {@link MessageReader} and {@link MessageWriter} instances based on Qpid Proton {@link Message}.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public final class ProtonAmqpMessageFactory {
|
||||
|
||||
private ProtonAmqpMessageFactory() {
|
||||
// prevent instantiation
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@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) throws CloudEventRWException {
|
||||
return createReader(message.getContentType(), message.getApplicationProperties(), message.getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@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 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 {@link MessageWriter} capable of translating both a structured and binary CloudEvent
|
||||
* to a proton-based AMQP 1.0 {@link 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<>();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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.amqp.impl;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.qpid.proton.amqp.messaging.AmqpValue;
|
||||
import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
|
||||
import org.apache.qpid.proton.amqp.messaging.Data;
|
||||
import org.apache.qpid.proton.amqp.messaging.Section;
|
||||
|
||||
import io.cloudevents.core.message.impl.MessageUtils;
|
||||
|
||||
/**
|
||||
* Constants and methods used throughout the Proton-based implementation of the AMQP 1.0 protocol
|
||||
* binding for cloud events.
|
||||
*/
|
||||
public final class AmqpConstants {
|
||||
|
||||
private AmqpConstants() {
|
||||
// prevent instantiation
|
||||
}
|
||||
|
||||
/**
|
||||
* The prefix name for CloudEvent attributes for use in the <em>application-properties</em> section
|
||||
* of an AMQP 1.0 message.
|
||||
*/
|
||||
public static final String CE_PREFIX = "cloudEvents:";
|
||||
|
||||
/**
|
||||
* The AMQP 1.0 <em>content-type</em> message property
|
||||
*/
|
||||
public static final String PROPERTY_CONTENT_TYPE = "content-type";
|
||||
|
||||
/**
|
||||
* Map a cloud event attribute name to a value. All values except the <em>datacontenttype</em> attribute are prefixed
|
||||
* with "cloudEvents:" as mandated by the spec.
|
||||
*/
|
||||
public static final Map<String, String> ATTRIBUTES_TO_PROPERTYNAMES = MessageUtils.generateAttributesToHeadersMapping(CEA -> {
|
||||
if (CEA.equals("datacontenttype")) {
|
||||
return PROPERTY_CONTENT_TYPE;
|
||||
}
|
||||
// prefix the attribute
|
||||
return CE_PREFIX + CEA;
|
||||
});
|
||||
|
||||
public static final String APP_PROPERTY_SPEC_VERSION = ATTRIBUTES_TO_PROPERTYNAMES.get("specversion");
|
||||
|
||||
/**
|
||||
* Gets the value of a specific <em>application property</em>.
|
||||
*
|
||||
* @param <T> The expected type of the property to retrieve.
|
||||
* @param props The application properties to retrieve the value from.
|
||||
* @param name The name of the application property.
|
||||
* @param type The expected value type.
|
||||
* @return The value or {@code null} if the properties do not contain a value of the expected type for the given
|
||||
* name.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getApplicationProperty(final ApplicationProperties props, final String name,
|
||||
final Class<T> type) {
|
||||
|
||||
if (props == null) {
|
||||
return null;
|
||||
} else {
|
||||
final Object value = props.getValue().get(name);
|
||||
if (type.isInstance(value)) {
|
||||
return (T) value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message payload into a byte array.
|
||||
* <p>
|
||||
* The bytes in the array are determined as follows:
|
||||
* <ul>
|
||||
* <li>If the body is a Data section, the bytes contained in the
|
||||
* Data section are returned.</li>
|
||||
* <li>If the body is an AmqpValue section and contains a byte array,
|
||||
* the bytes in the array are returned.</li>
|
||||
* <li>If the body is an AmqpValue section and contains a non-empty String,
|
||||
* the UTF-8 encoding of the String is returned.</li>
|
||||
* <li>In all other cases, {@code null} is returned.</li>
|
||||
* </ul>
|
||||
|
||||
* @param payload The message payload to extract the bytes from.
|
||||
* @return The payload bytes or {@code null} if the above stated conditions are not met.
|
||||
*/
|
||||
public static byte[] getPayloadAsByteArray(final Section payload) {
|
||||
if (payload == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (payload instanceof Data) {
|
||||
final Data body = (Data) payload;
|
||||
return body.getValue().getArray();
|
||||
} else if (payload instanceof AmqpValue) {
|
||||
final AmqpValue body = (AmqpValue) payload;
|
||||
if (body.getValue() instanceof byte[]) {
|
||||
return (byte[]) body.getValue();
|
||||
} else if (body.getValue() instanceof String &&
|
||||
((String) body.getValue()).length() > 0 ) {
|
||||
return ((String) body.getValue()).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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.amqp.impl;
|
||||
|
||||
import io.cloudevents.SpecVersion;
|
||||
import io.cloudevents.core.data.BytesCloudEventData;
|
||||
import io.cloudevents.core.message.impl.BaseGenericBinaryMessageReaderImpl;
|
||||
import org.apache.qpid.proton.amqp.messaging.ApplicationProperties;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* An AMQP 1.0 message reader that can be read as a <em>CloudEvent</em>.
|
||||
* <p>
|
||||
*
|
||||
* This reader reads sections of an AMQP message to construct a CloudEvent representation by doing the following:
|
||||
* <ul>
|
||||
* <li> If the content-type property is set for an AMQP message, the value of the property
|
||||
* is represented as a cloud event datacontenttype attribute.
|
||||
* <li> If the (mandatory) application-properties of the AMQP message contains attributes and/or extentions,
|
||||
* this reader will represent each property/extension as a cloud event attribute.
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
public final class ProtonAmqpBinaryMessageReader extends BaseGenericBinaryMessageReaderImpl<String, Object> {
|
||||
|
||||
private final String contentType;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
/**
|
||||
* Create an instance of an AMQP message reader.
|
||||
*
|
||||
* @param version The version of the cloud event message.
|
||||
* @param applicationProperties The application properties of the AMQP message that contains
|
||||
* the cloud event metadata (i.e attributes and extensions).
|
||||
* The applicationProperties MUST not be {@code null}.
|
||||
* @param contentType The content-type property of the AMQP message or {@code null} if the message content type is unknown.
|
||||
* @param payload The message payload or {@code null} if the message does not contain any payload.
|
||||
*
|
||||
* @throws NullPointerException if the applicationPropereties is {@code null}.
|
||||
*/
|
||||
public ProtonAmqpBinaryMessageReader(final SpecVersion version, final ApplicationProperties applicationProperties,
|
||||
final String contentType, final byte[] payload) {
|
||||
super(version, payload != null && payload.length > 0 ? BytesCloudEventData.wrap(payload) : null);
|
||||
this.contentType = contentType;
|
||||
this.applicationProperties = Objects.requireNonNull(applicationProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isContentTypeHeader(final String key) {
|
||||
return key.equals(AmqpConstants.PROPERTY_CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the given attribute key is prefixed with <em>cloudEvents:</em>
|
||||
*
|
||||
* @param key The key to test for the presence of the prefix.
|
||||
* @return True if the specified key starts with the prefix or
|
||||
* false otherwise.
|
||||
*/
|
||||
@Override
|
||||
protected boolean isCloudEventsHeader(final String key) {
|
||||
final int prefixLength = AmqpConstants.CE_PREFIX.length();
|
||||
return key.length() > prefixLength && key.startsWith(AmqpConstants.CE_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cloud event attribute key without the preceding prefix.
|
||||
*
|
||||
* @param key The key containing the AMQP specific prefix.
|
||||
*
|
||||
* @return The key without the prefix.
|
||||
*/
|
||||
@Override
|
||||
protected String toCloudEventsKey(final String key) {
|
||||
return key.substring(AmqpConstants.CE_PREFIX.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the <em>content-type</em> message property and all <em>application-properties</em> of this message reader.
|
||||
* <p>
|
||||
* This method only visits properties containing a name and value which are not {@code null}.
|
||||
*
|
||||
* @param fn A callback to consume this reader's application-properties
|
||||
* and content-type property.
|
||||
*/
|
||||
@Override
|
||||
protected void forEachHeader(final BiConsumer<String, Object> fn) {
|
||||
if (contentType != null) {
|
||||
// visit the content-type message property
|
||||
fn.accept(AmqpConstants.PROPERTY_CONTENT_TYPE, contentType);
|
||||
}
|
||||
// visit application-properties
|
||||
applicationProperties.getValue().forEach((k, v) -> {
|
||||
if (k != null && v != null) {
|
||||
fn.accept(k, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cloud event representation of the value.
|
||||
* <p>
|
||||
* This method simply returns the string representation of the type of value passed as argument.
|
||||
*
|
||||
* @param value The value of a CloudEvent attribute or extension.
|
||||
*
|
||||
* @return The string representation of the specified value.
|
||||
*/
|
||||
@Override
|
||||
protected String toCloudEventsValue(final Object value) {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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.amqp.impl;
|
||||
|
||||
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.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
|
||||
* mandated by the AMQP 1.0 protocol binding specification for cloud events.
|
||||
* <p>
|
||||
* This writer returns an AMQP message at the end of the write process.
|
||||
*/
|
||||
public final class ProtonAmqpMessageWriter<R> implements MessageWriter<CloudEventWriter<Message>, Message>, CloudEventWriter<Message> {
|
||||
|
||||
private ApplicationProperties applicationProperties;
|
||||
private Message message;
|
||||
|
||||
/**
|
||||
* Creates a proton-base message writer.
|
||||
*/
|
||||
public ProtonAmqpMessageWriter() {
|
||||
message = Message.Factory.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
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");
|
||||
}
|
||||
String propName = AmqpConstants.ATTRIBUTES_TO_PROPERTYNAMES.get(name);
|
||||
if (propName == null) {
|
||||
propName = name;
|
||||
}
|
||||
applicationProperties.getValue().put(propName, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtonAmqpMessageWriter<R> create(final SpecVersion version) {
|
||||
if (applicationProperties == null) {
|
||||
applicationProperties = new ApplicationProperties(new HashMap<>());
|
||||
}
|
||||
applicationProperties.getValue().put(AmqpConstants.APP_PROPERTY_SPEC_VERSION, version.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message setEvent(final EventFormat format, final byte[] value) throws CloudEventRWException {
|
||||
message.setContentType(format.serializedContentType());
|
||||
message.setBody(new Data(new Binary(value)));
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message end(final CloudEventData data) throws CloudEventRWException {
|
||||
message.setBody(new Data(new Binary(data.toBytes())));
|
||||
message.setApplicationProperties(applicationProperties);
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message end() {
|
||||
message.setBody(null);
|
||||
message.setApplicationProperties(applicationProperties);
|
||||
return message;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* 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.amqp;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.SpecVersion;
|
||||
import io.cloudevents.amqp.impl.AmqpConstants;
|
||||
import io.cloudevents.core.message.Encoding;
|
||||
import io.cloudevents.core.message.MessageReader;
|
||||
import io.cloudevents.core.mock.CSVFormat;
|
||||
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}.
|
||||
*/
|
||||
public class ProtonAmqpMessageFactoryTest {
|
||||
|
||||
private static final String PREFIX_TEMPLATE = AmqpConstants.CE_PREFIX + "%s";
|
||||
private static final String DATACONTENTTYPE_NULL = null;
|
||||
private static final byte[] DATAPAYLOAD_NULL = null;
|
||||
|
||||
@ParameterizedTest()
|
||||
@MethodSource("binaryTestArguments")
|
||||
public void readBinary(final Map<String, Object> props, final String contentType, final byte[] body,
|
||||
final CloudEvent event) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ParameterizedTest()
|
||||
@MethodSource("io.cloudevents.core.test.Data#allEventsWithoutExtensions")
|
||||
public void readStructured(final CloudEvent event) {
|
||||
final String contentType = CSVFormat.INSTANCE.serializedContentType() + "; charset=utf8";
|
||||
final byte[] contentPayload = CSVFormat.INSTANCE.serialize(event);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> binaryTestArguments() {
|
||||
|
||||
return Stream.of(
|
||||
// V03
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
|
||||
property(CloudEventV03.ID, Data.ID),
|
||||
property(CloudEventV03.TYPE, Data.TYPE),
|
||||
property(CloudEventV03.SOURCE, Data.SOURCE),
|
||||
property("ignored", "ignore")
|
||||
),
|
||||
DATACONTENTTYPE_NULL,
|
||||
DATAPAYLOAD_NULL,
|
||||
Data.V03_MIN
|
||||
),
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
|
||||
property(CloudEventV03.ID, Data.ID),
|
||||
property(CloudEventV03.TYPE, Data.TYPE),
|
||||
property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
|
||||
property(CloudEventV03.SCHEMAURL, Data.DATASCHEMA.toString()),
|
||||
property(CloudEventV03.SUBJECT, Data.SUBJECT),
|
||||
property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
|
||||
property("ignored", "ignore")
|
||||
),
|
||||
Data.DATACONTENTTYPE_JSON,
|
||||
Data.DATA_JSON_SERIALIZED,
|
||||
Data.V03_WITH_JSON_DATA
|
||||
),
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
|
||||
property(CloudEventV03.ID, Data.ID),
|
||||
property(CloudEventV03.TYPE, Data.TYPE),
|
||||
property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
|
||||
property(CloudEventV03.SCHEMAURL, Data.DATASCHEMA.toString()),
|
||||
property(CloudEventV03.SUBJECT, Data.SUBJECT),
|
||||
property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
|
||||
property("astring", "aaa"),
|
||||
property("aboolean", "true"),
|
||||
property("anumber", "10"),
|
||||
property("ignored", "ignored")
|
||||
),
|
||||
Data.DATACONTENTTYPE_JSON,
|
||||
Data.DATA_JSON_SERIALIZED,
|
||||
Data.V03_WITH_JSON_DATA_WITH_EXT_STRING
|
||||
),
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
|
||||
property(CloudEventV03.ID, Data.ID),
|
||||
property(CloudEventV03.TYPE, Data.TYPE),
|
||||
property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
|
||||
property(CloudEventV03.SUBJECT, Data.SUBJECT),
|
||||
property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
|
||||
property("ignored", "ignored")
|
||||
),
|
||||
Data.DATACONTENTTYPE_XML,
|
||||
Data.DATA_XML_SERIALIZED,
|
||||
Data.V03_WITH_XML_DATA
|
||||
),
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
|
||||
property(CloudEventV03.ID, Data.ID),
|
||||
property(CloudEventV03.TYPE, Data.TYPE),
|
||||
property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
|
||||
property(CloudEventV03.SUBJECT, Data.SUBJECT),
|
||||
property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
|
||||
property("ignored", "ignored")
|
||||
),
|
||||
Data.DATACONTENTTYPE_TEXT,
|
||||
Data.DATA_TEXT_SERIALIZED,
|
||||
Data.V03_WITH_TEXT_DATA
|
||||
),
|
||||
// V1
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
|
||||
property(CloudEventV1.ID, Data.ID),
|
||||
property(CloudEventV1.TYPE, Data.TYPE),
|
||||
property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
|
||||
property("ignored", "ignored")
|
||||
),
|
||||
DATACONTENTTYPE_NULL,
|
||||
DATAPAYLOAD_NULL,
|
||||
Data.V1_MIN
|
||||
),
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
|
||||
property(CloudEventV1.ID, Data.ID),
|
||||
property(CloudEventV1.TYPE, Data.TYPE),
|
||||
property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
|
||||
property(CloudEventV1.DATASCHEMA, Data.DATASCHEMA.toString()),
|
||||
property(CloudEventV1.SUBJECT, Data.SUBJECT),
|
||||
property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
|
||||
property("ignored", "ignored")
|
||||
),
|
||||
Data.DATACONTENTTYPE_JSON,
|
||||
Data.DATA_JSON_SERIALIZED,
|
||||
Data.V1_WITH_JSON_DATA
|
||||
),
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
|
||||
property(CloudEventV1.ID, Data.ID),
|
||||
property(CloudEventV1.TYPE, Data.TYPE),
|
||||
property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
|
||||
property(CloudEventV1.DATASCHEMA, Data.DATASCHEMA.toString()),
|
||||
property(CloudEventV1.SUBJECT, Data.SUBJECT),
|
||||
property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
|
||||
property("astring", "aaa"),
|
||||
property("aboolean", "true"),
|
||||
property("anumber", "10"),
|
||||
property("ignored", "ignored")
|
||||
),
|
||||
Data.DATACONTENTTYPE_JSON,
|
||||
Data.DATA_JSON_SERIALIZED,
|
||||
Data.V1_WITH_JSON_DATA_WITH_EXT_STRING
|
||||
),
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
|
||||
property(CloudEventV1.ID, Data.ID),
|
||||
property(CloudEventV1.TYPE, Data.TYPE),
|
||||
property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
|
||||
property(CloudEventV1.SUBJECT, Data.SUBJECT),
|
||||
property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
|
||||
property("ignored", "ignored")
|
||||
),
|
||||
Data.DATACONTENTTYPE_XML,
|
||||
Data.DATA_XML_SERIALIZED,
|
||||
Data.V1_WITH_XML_DATA
|
||||
),
|
||||
Arguments.of(
|
||||
properties(
|
||||
property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
|
||||
property(CloudEventV1.ID, Data.ID),
|
||||
property(CloudEventV1.TYPE, Data.TYPE),
|
||||
property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
|
||||
property(CloudEventV1.SUBJECT, Data.SUBJECT),
|
||||
property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
|
||||
property("ignored", "ignored")
|
||||
),
|
||||
Data.DATACONTENTTYPE_TEXT,
|
||||
Data.DATA_TEXT_SERIALIZED,
|
||||
Data.V1_WITH_TEXT_DATA
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static final SimpleEntry<String, Object> property(final String name, final Object value) {
|
||||
return name.equalsIgnoreCase("ignored") ?
|
||||
new SimpleEntry<>(name, value) :
|
||||
new SimpleEntry<>(String.format(PREFIX_TEMPLATE, name), value);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static final Map<String, Object> properties(final SimpleEntry<String, Object>... entries) {
|
||||
return Stream.of(entries)
|
||||
.collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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.amqp;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
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.apache.qpid.proton.message.Message;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.amqp.impl.ProtonAmqpMessageWriter;
|
||||
import io.cloudevents.core.format.EventFormat;
|
||||
import io.cloudevents.core.message.MessageWriter;
|
||||
import io.cloudevents.core.mock.CSVFormat;
|
||||
|
||||
/**
|
||||
* Tests verifying the behavior of the {@code ProtonAmqpMessageWriter}.
|
||||
*/
|
||||
public class ProtonAmqpMessageWriterTest {
|
||||
|
||||
/**
|
||||
* Verifies that a binary CloudEvent message can be successfully represented
|
||||
* as an AMQP message.
|
||||
*/
|
||||
@ParameterizedTest()
|
||||
@MethodSource("io.cloudevents.core.test.Data#allEventsWithStringExtensions")
|
||||
public void testWriteBinaryCloudEventToAmqpRepresentation(final CloudEvent binaryEvent) {
|
||||
|
||||
final Message expectedMessage = translateBinaryEvent(binaryEvent);
|
||||
|
||||
final MessageWriter<?, Message> writer = new ProtonAmqpMessageWriter<Message>();
|
||||
final Message actualMessage = writer.writeBinary(binaryEvent);
|
||||
|
||||
assertThat(actualMessage.getContentType()).isEqualTo(expectedMessage.getContentType());
|
||||
assertThat(Objects.toString(actualMessage.getBody())).isEqualTo(Objects.toString(expectedMessage.getBody()));
|
||||
assertThat(actualMessage.getApplicationProperties().getValue()).
|
||||
isEqualTo(expectedMessage.getApplicationProperties().getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a structured CloudEvent message (in CSV format) can be successfully represented
|
||||
* as an AMQP message.
|
||||
*/
|
||||
@ParameterizedTest()
|
||||
@MethodSource("io.cloudevents.core.test.Data#allEventsWithoutExtensions")
|
||||
public void testWriteStructuredCloudEventToAmqpRepresentation(final CloudEvent event) {
|
||||
final EventFormat format = CSVFormat.INSTANCE;
|
||||
final Message expectedMessage = translateStructured(event, format);
|
||||
|
||||
final MessageWriter<?, Message> writer = new ProtonAmqpMessageWriter<Message>();
|
||||
final Message actualMessage = writer.writeStructured(event, format.serializedContentType());
|
||||
|
||||
assertThat(actualMessage.getContentType()).isEqualTo(expectedMessage.getContentType());
|
||||
assertThat(Objects.toString(actualMessage.getBody())).isEqualTo(Objects.toString(expectedMessage.getBody()));
|
||||
assertThat(actualMessage.getApplicationProperties()).isNull();
|
||||
}
|
||||
|
||||
private Message translateBinaryEvent(final CloudEvent event) {
|
||||
|
||||
final Message message = Message.Factory.create();
|
||||
|
||||
final Map<String, Object> map = new HashMap<>();
|
||||
|
||||
if (!event.getAttributeNames().isEmpty()) {
|
||||
event.getAttributeNames().forEach(name -> {
|
||||
if (name.equals("datacontentencoding")) {
|
||||
// ignore
|
||||
} else if (name.equals("datacontenttype") && event.getAttribute(name) != null) {
|
||||
message.setContentType(event.getAttribute(name).toString());
|
||||
} else {
|
||||
addProperty(map, name, Objects.toString(event.getAttribute(name)), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!event.getExtensionNames().isEmpty()) {
|
||||
event.getExtensionNames().forEach(name -> addProperty(map, name, Objects.toString(event.getExtension(name)), false));
|
||||
}
|
||||
|
||||
if (event.getData() != null) {
|
||||
final Section payload = new org.apache.qpid.proton.amqp.messaging.Data(new Binary(event.getData().toBytes()));
|
||||
message.setBody(payload);
|
||||
}
|
||||
|
||||
message.setApplicationProperties(new ApplicationProperties(map));
|
||||
return message;
|
||||
}
|
||||
|
||||
private Message translateStructured(final CloudEvent event, final EventFormat format) {
|
||||
final Message message = Message.Factory.create();
|
||||
message.setContentType(format.serializedContentType());
|
||||
message.setBody(new org.apache.qpid.proton.amqp.messaging.Data(new Binary(format.serialize(event))));
|
||||
return message;
|
||||
}
|
||||
private void addProperty(final Map<String, Object> map, final String name, final String value, final boolean prefix) {
|
||||
if (prefix) {
|
||||
map.put(String.format("cloudEvents:%s", name), value);
|
||||
} else {
|
||||
map.put(name, value);
|
||||
}
|
||||
}
|
||||
}
|
545
api/README.md
545
api/README.md
|
@ -1,546 +1,5 @@
|
|||
# CloudEvents API
|
||||
|
||||
The base classes, interfaces and low-level APIs to use CloudEvents.
|
||||
Javadoc: [](http://www.javadoc.io/doc/io.cloudevents/cloudevents-api)
|
||||
|
||||
## How to Use
|
||||
|
||||
Here we will see how to use the pre-configure marshallers and unmarshallers.
|
||||
|
||||
### Binary Marshaller
|
||||
|
||||
The high-level API to marshal CloudEvents as binary content mode.
|
||||
|
||||
```java
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.format.Wire;
|
||||
import io.cloudevents.v1.CloudEventBuilder;
|
||||
import io.cloudevents.v1.CloudEventImpl;
|
||||
import io.cloudevents.v1.http.Marshallers;
|
||||
|
||||
//...
|
||||
|
||||
/*Create a tracing extension*/
|
||||
final DistributedTracingExtension dt =
|
||||
new DistributedTracingExtension();
|
||||
|
||||
dt.setTraceparent("0");
|
||||
dt.setTracestate("congo=4");
|
||||
|
||||
/*Format it as extension format*/
|
||||
final ExtensionFormat tracing =
|
||||
new DistributedTracingExtension.Format(dt);
|
||||
|
||||
/* Build a CloudEvent instance */
|
||||
CloudEventImpl<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withType("com.github.pull.create")
|
||||
.withSource(URI.create("https://github.com/cloudevents/spec/pull"))
|
||||
.withId("A234-1234-1234")
|
||||
.withDataschema(URI.create("http://my.br"))
|
||||
.withTime(ZonedDateTime.now())
|
||||
.withDataContentType("text/plain")
|
||||
.withData("my-data")
|
||||
.withExtension(tracing)
|
||||
.build();
|
||||
|
||||
/* Marshal the event as a Wire instance */
|
||||
Wire<String, String, String> wire =
|
||||
Marshallers.<String>
|
||||
binary()
|
||||
.withEvent(() -> ce)
|
||||
.marshal();
|
||||
|
||||
/*
|
||||
* Use the wire result, getting the headers map
|
||||
* and the actual payload
|
||||
*/
|
||||
wire.getHeaders(); //Map<String, String>
|
||||
wire.getPayload(); //Optional<String> which has the JSON
|
||||
|
||||
// Use in the transport binding: http, kafka, etc ...
|
||||
```
|
||||
|
||||
### Binary Umarshaller
|
||||
|
||||
The high-level API to unmarshal CloudEvents from binary content mode.
|
||||
|
||||
```java
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
import io.cloudevents.v1.AttributesImpl;
|
||||
import io.cloudevents.v1.CloudEventBuilder;
|
||||
import io.cloudevents.v1.http.Unmarshallers;
|
||||
|
||||
// . . .
|
||||
|
||||
/* The HTTP headers example */
|
||||
Map<String, Object> httpHeaders = new HashMap<>();
|
||||
httpHeaders.put("ce-specversion", "0.2");
|
||||
httpHeaders.put("ce-type", "com.github.pull.create");
|
||||
httpHeaders.put("ce-source", "https://github.com/cloudevents/spec/pull");
|
||||
httpHeaders.put("ce-id", "A234-1234-1234");
|
||||
httpHeaders.put("ce-time", "2018-04-05T17:31:00Z");
|
||||
httpHeaders.put("ce-dataschema", "http://my.br");
|
||||
httpHeaders.put("my-ext", "my-custom extension");
|
||||
httpHeaders.put("traceparent", "0");
|
||||
httpHeaders.put("tracestate", "congo=4");
|
||||
httpHeaders.put("Content-Type", "application/json");
|
||||
|
||||
/* The payload */
|
||||
String myPayload = "{\"foo\" : \"rocks\", \"name\" : \"jocker\"}";
|
||||
|
||||
/* Unmarshals as CloudEvent instance */
|
||||
CloudEvent<AttributesImpl, Map> event =
|
||||
Unmarshallers.binary(Map.class)
|
||||
.withHeaders(() -> httpHeaders)
|
||||
.withPayload(() -> myPayload)
|
||||
.unmarshal();
|
||||
|
||||
/* Use the CloudEvent instance attributes, data and extensions */
|
||||
event.getAttributes();
|
||||
event.getData();
|
||||
event.getExtensions();
|
||||
```
|
||||
|
||||
### Structured Marshaller
|
||||
|
||||
The high-level API to marshal CloudEvents as structured content mode.
|
||||
|
||||
```java
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.v1.CloudEventBuilder;
|
||||
import io.cloudevents.v1.CloudEventImpl;
|
||||
import io.cloudevents.v1.http.Marshallers;
|
||||
|
||||
// . . .
|
||||
|
||||
final DistributedTracingExtension dt =
|
||||
new DistributedTracingExtension();
|
||||
dt.setTraceparent("0");
|
||||
dt.setTracestate("congo=4");
|
||||
|
||||
final ExtensionFormat tracing =
|
||||
new DistributedTracingExtension.Format(dt);
|
||||
|
||||
final CloudEventImpl<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withType("com.github.pull.create")
|
||||
.withSource(URI.create("https://github.com/cloudevents/spec/pull"))
|
||||
.withId("A234-1234-1234")
|
||||
.withDataschema(URI.create("http://my.br"))
|
||||
.withTime(ZonedDateTime.now())
|
||||
.withDataContentType("text/plain")
|
||||
.withData("my-data")
|
||||
.withExtension(tracing)
|
||||
.build();
|
||||
|
||||
final Wire<String, String, String> wire =
|
||||
Marshallers.<String>structured()
|
||||
.withEvent(() -> ce)
|
||||
.marshal();
|
||||
|
||||
/*
|
||||
* Use the wire result, getting the headers map
|
||||
* and the actual payload
|
||||
*/
|
||||
wire.getHeaders(); //Map<String, String>
|
||||
wire.getPayload(); //Optional<String> which has the JSON
|
||||
|
||||
// Use in the transport binding: http, kafka, etc ...
|
||||
```
|
||||
|
||||
### Structured Unmarshaller
|
||||
|
||||
The high-level API to unmarshal CloudEvents as structured content mode.
|
||||
|
||||
```java
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.v1.AttributesImpl;
|
||||
import io.cloudevents.v1.http.Unmarshallers;
|
||||
|
||||
// . . .
|
||||
|
||||
/* The HTTP Headers */
|
||||
Map<String, Object> httpHeaders = new HashMap<>();
|
||||
httpHeaders.put("Content-Type", "application/cloudevents+json");
|
||||
|
||||
/* Distributed Tracing */
|
||||
httpHeaders.put("traceparent", "0x200");
|
||||
httpHeaders.put("tracestate", "congo=9");
|
||||
|
||||
|
||||
/* JSON Payload */
|
||||
String payload = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\"}";
|
||||
|
||||
/* Unmarshalling . . . */
|
||||
CloudEvent<AttributesImpl, Map> event =
|
||||
Unmarshallers.structured(Map.class)
|
||||
.withHeaders(() -> httpHeaders)
|
||||
.withPayload(() -> payload)
|
||||
.unmarshal();
|
||||
|
||||
/* Use the event instance */
|
||||
event.getAttributes();
|
||||
event.getExtensions();
|
||||
event.getData();
|
||||
```
|
||||
|
||||
## Low-level (Un)Marshalling
|
||||
|
||||
When you need to process different formats, instead of JSON, or if you have a custom extension implementation. You must develop your own (un)marshallers.
|
||||
|
||||
We provide a way to make it easy, well, or with less pain at all.
|
||||
|
||||
Let us introduce the step builders:
|
||||
|
||||
- [BinaryMarshaller](./src/main/java/io/cloudevents/format/BinaryMarshaller.java)
|
||||
- [StructuredMarshaller](./src/main/java/io/cloudevents/format/StructuredMarshaller.java)
|
||||
- [BinaryUnmarshaller](./src/main/java/io/cloudevents/format/BinaryUnmarshaller.java)
|
||||
- [StructuredUnmarshaller](./src/main/java/io/cloudevents/format/StructuredUnmarshaller.java)
|
||||
|
||||
All of them follow the [step builder pattern](https://java-design-patterns.com/patterns/step-builder/), that helps a lot when you are new in the data (un)marshalling. To support them we have a [bunch of functional interfaces](./src/main/java/io/cloudevents/fun), used by each step.
|
||||
|
||||
### Marshaller
|
||||
|
||||
Well, this is how build marshaller using the low-level API.
|
||||
|
||||
**Binary Marshaller**
|
||||
|
||||
```java
|
||||
/*
|
||||
* The imports used by the example bellow
|
||||
*/
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.format.BinaryMarshaller;
|
||||
import io.cloudevents.format.Wire;
|
||||
import io.cloudevents.format.builder.EventStep;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.json.types.Much;
|
||||
import io.cloudevents.v1.Accessor;
|
||||
import io.cloudevents.v1.AttributesImpl;
|
||||
|
||||
// . . .
|
||||
|
||||
/*
|
||||
* Step 0. Define the types - there are four
|
||||
* - Type 1 -> AttributesImpl: the implementation of attributes
|
||||
* - Type 2 -> Much..........: the type CloudEvents' 'data'
|
||||
* - Type 3 -> String........: the type of payload that will result of marshalling
|
||||
* - Type 4 -> String........: the type of headers values. String for HTTP, byte[] for Kafka . . .
|
||||
*/
|
||||
EventStep<AttributesImpl, Much, String, String> builder =
|
||||
BinaryMarshaller.<AttributesImpl, Much, String, String>
|
||||
builder()
|
||||
/*
|
||||
* Step 1. The attributes marshalling
|
||||
* - in this step we must provide an impl able to marshal AttributesImpl into a
|
||||
* Map<String, String>
|
||||
*/
|
||||
.map(AttributesImpl::marshal)
|
||||
|
||||
/*
|
||||
* Step 2. Access the internal list of extensions
|
||||
* - here we must provide an accessor for the internal list of extensions
|
||||
*/
|
||||
.map(Accessor::extensionsOf)
|
||||
|
||||
/*
|
||||
* Step 3. The extensions marshalling
|
||||
* - we must provide an impl able to marshal a Collection<ExtensionFormat> into a
|
||||
* Map<String, String>
|
||||
*/
|
||||
.map(ExtensionFormat::marshal)
|
||||
|
||||
/*
|
||||
* Step 4. Mapping to headers
|
||||
* - provide an impl able to map from attributes and extensions into a
|
||||
* Map<String, String>, the headers of transport binding.
|
||||
*/
|
||||
.map(HeaderMapper::map)
|
||||
|
||||
/*
|
||||
* Step 5. The data marshaller
|
||||
* - provider an impl able to marshal the CloudEvents' data into payload
|
||||
* for transport
|
||||
*/
|
||||
.map(Json.<Much, String>marshaller()::marshal)
|
||||
|
||||
/*
|
||||
* Step 6. The wire builder
|
||||
* - to make easy to get the marshalled paylaod and the headers we
|
||||
* have the Wire
|
||||
* - here must to provide a way to create new instance of Wire
|
||||
* - now we get the EventStep<AttributesImpl, Much, String, String>, a common step
|
||||
* that every marshaller returns
|
||||
* - from here we just call withEvent() and marshal() methods
|
||||
*/
|
||||
.builder(Wire<String, String, String>::new);
|
||||
|
||||
/*
|
||||
* Using the marshaller
|
||||
*/
|
||||
Wire<String, String, String> wire =
|
||||
builder
|
||||
.withEvent(() -> myEvent)
|
||||
.marshal();
|
||||
}
|
||||
```
|
||||
|
||||
**Structured Marshaller**
|
||||
|
||||
```java
|
||||
/*
|
||||
* The imports used by the example bellow
|
||||
*/
|
||||
import java.util.HashMap;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.format.StructuredMarshaller;
|
||||
import io.cloudevents.format.builder.EventStep;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.json.types.Much;
|
||||
import io.cloudevents.v1.Accessor;
|
||||
import io.cloudevents.v1.AttributesImpl;
|
||||
|
||||
// . . .
|
||||
|
||||
/*
|
||||
* Step 0. Define the types - there are four
|
||||
* - Type 1 -> AttributesImpl: the implementation of attributes
|
||||
* - Type 2 -> Much..........: the type CloudEvents' 'data'
|
||||
* - Type 3 -> String........: the type of payload that will result of marshalling
|
||||
* - Type 4 -> String........: the type of headers values. String for HTTP, byte[] for Kafka . . .
|
||||
*/
|
||||
EventStep<AttributesImpl, Much, String, String> builder =
|
||||
StructuredMarshaller.<AttributesImpl, Much, String, String>
|
||||
builder()
|
||||
/*
|
||||
* Step 1. Setting the media type for the envelope
|
||||
* - here we must to say the name of media type header and it's value
|
||||
*/
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
|
||||
/*
|
||||
* Step 2. The marshaller for envelope
|
||||
* - we must provide an impl able to marshal the cloudevents envelope
|
||||
*/
|
||||
.map((event) -> {
|
||||
return Json.<CloudEvent<AttributesImpl, Much>, String>
|
||||
marshaller().marshal(event, new HashMap<>());
|
||||
})
|
||||
|
||||
/*
|
||||
* Step 3. Access the internal list of extensions
|
||||
* - here we must provide an accessor for the internal list of extensions
|
||||
*/
|
||||
.map(Accessor::extensionsOf)
|
||||
|
||||
/*
|
||||
* Step 4. The extensions marshalling
|
||||
* - we must provide an impl able to marshal a Collection<ExtensionFormat> into a Map<String, String>
|
||||
*/
|
||||
.map(ExtensionFormat::marshal)
|
||||
|
||||
/*
|
||||
* Step 5. Mapping to headers
|
||||
* - provide an impl able to map from attributes and extensions into a Map<String, String>, the headers of transport binding.
|
||||
* - now we get the EventStep<AttributesImpl, Much, String, String>, a common step that every marshaller returns
|
||||
* - from here we just call withEvent() and marshal() methods
|
||||
*/
|
||||
.map(HeaderMapper::map);
|
||||
|
||||
/*
|
||||
* Using the marshaller
|
||||
*/
|
||||
Wire<String, String, String> wire =
|
||||
builder
|
||||
.withEvent(() -> myEvent)
|
||||
.marshal();
|
||||
|
||||
```
|
||||
|
||||
### Unmarshaller
|
||||
|
||||
**Binary Unmarshaller**
|
||||
|
||||
```java
|
||||
/*
|
||||
* The imports used by the example bellow
|
||||
*/
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
import io.cloudevents.format.BinaryUnmarshaller;
|
||||
import io.cloudevents.format.builder.HeadersStep;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.json.types.Much;
|
||||
import io.cloudevents.v1.AttributesImpl;
|
||||
import io.cloudevents.v1.CloudEventBuilder;
|
||||
|
||||
// . . .
|
||||
|
||||
/*
|
||||
* Step 0. Define the types - there are three
|
||||
* - Type 1 -> AttributesImpl: the implementation of attributes
|
||||
* - Type 2 -> Much..........: the type CloudEvents' 'data'
|
||||
* - Type 3 -> String........: the type of payload used in the unmarshalling
|
||||
*/
|
||||
HeadersStep<AttributesImpl, Much, String> builder =
|
||||
BinaryUnmarshaller.<AttributesImpl, Much, String>
|
||||
builder()
|
||||
/*
|
||||
* Step 1. Mapping from headers Map to attributes one
|
||||
* - we must provide a mapper able to map from transport headers to a map of attributes
|
||||
* - this like a translation, removing the prefixes or unknow names for example
|
||||
*/
|
||||
.map(AttributeMapper::map)
|
||||
|
||||
/*
|
||||
* Step 2. The attributes ummarshalling
|
||||
* - we must provide an impl able to unmarshal a Map of attributes into
|
||||
* an instance of Attributes
|
||||
*/
|
||||
.map(AttributesImpl::unmarshal)
|
||||
|
||||
/*
|
||||
* Step 3. The data umarshaller
|
||||
* - we may provive more than one unmarshaller per media type
|
||||
* - we must provide an impl able to unmashal the payload into
|
||||
* the actual 'data' type instance
|
||||
*/
|
||||
.map("application/json", Json.umarshaller(Much.class)::unmarshal)
|
||||
|
||||
/*
|
||||
* Step 3'. Another data unmarshaller
|
||||
*/
|
||||
.map("media type", (payload, headers) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
|
||||
/*
|
||||
* When we are ok with data unmarshallers we call next()
|
||||
*/
|
||||
.next()
|
||||
|
||||
/*
|
||||
* Step 4. The extension mapping
|
||||
* - we must provider an impl able to map from transport headers to map of extensions
|
||||
* - we may use this for extensions that lives in the transport headers
|
||||
*/
|
||||
.map(ExtensionMapper::map)
|
||||
|
||||
/*
|
||||
* Step 5. The extension unmarshaller
|
||||
* - we must provide an impl able to unmarshal from map of extenstions into
|
||||
* actual ones
|
||||
*/
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
|
||||
/*
|
||||
* Step 5'. Another extension unmarshaller
|
||||
*/
|
||||
.map((extensionsMap) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
|
||||
/*
|
||||
* When we are ok with extensions unmarshallers we call next()
|
||||
*/
|
||||
.next()
|
||||
|
||||
/*
|
||||
* Step 6. The CloudEvent builder
|
||||
* - we must provide an impl able to take the extensions, data and attributes
|
||||
* and build CloudEvent instances
|
||||
* - now we get the HeadersStep<AttributesImpl, Much, String>, a common step
|
||||
* that event unmarshaller must returns
|
||||
* - from here we just call withHeaders(), withPayload() and unmarshal()
|
||||
*/
|
||||
.builder(CloudEventBuilder.<Much>builder()::build);
|
||||
|
||||
/*
|
||||
* Using the unmarshaller
|
||||
*/
|
||||
CloudEvent<AttributesImpl, Much> myEvent =
|
||||
builder
|
||||
.withHeaders(() -> transportHeaders)
|
||||
.withPayload(() -> payload)
|
||||
.unmarshal();
|
||||
```
|
||||
|
||||
**Structured Unmarshaller**
|
||||
|
||||
```java
|
||||
/*
|
||||
* Step 0. Define the types - there are three
|
||||
* - Type 1 -> AttributesImpl: the implementation of attributes
|
||||
* - Type 2 -> Much..........: the type CloudEvents' 'data'
|
||||
* - Type 3 -> String........: the type of payload used in the unmarshalling
|
||||
*/
|
||||
HeadersStep<AttributesImpl, Much, String> step =
|
||||
StructuredUnmarshaller.<AttributesImpl, Much, String>
|
||||
builder()
|
||||
/*
|
||||
* Step 1. The extension mapping
|
||||
* - we must provider an impl able to map from transport headers to map of extensions
|
||||
* - we may use this for extensions that lives in the transport headers
|
||||
*/
|
||||
.map(ExtensionMapper::map)
|
||||
|
||||
/*
|
||||
* Step 2. The extension unmarshaller
|
||||
* - we must provide an impl able to unmarshal from map of extenstions into actual ones
|
||||
*/
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
|
||||
/*
|
||||
* Step 2'. When we are ok with extension unmarshallers, call next()
|
||||
*/
|
||||
.next()
|
||||
|
||||
/*
|
||||
* Step 3. Envelope unmarshaller
|
||||
* - we must provide an impl able to unmarshal the envelope into cloudevents
|
||||
* - now we get the HeadersStep<AttributesImpl, Much, String>, a common step that event unmarshaller must returns
|
||||
* - from here we just call withHeaders(), withPayload() and unmarshal()
|
||||
*/
|
||||
.map((payload, extensions) -> {
|
||||
CloudEventImpl<Much> event =
|
||||
Json.<CloudEventImpl<Much>>
|
||||
decodeValue(payload, CloudEventImpl.class, Much.class);
|
||||
|
||||
CloudEventBuilder<Much> builder =
|
||||
CloudEventBuilder.<Much>builder(event);
|
||||
|
||||
extensions.get().forEach(extension -> {
|
||||
builder.withExtension(extension);
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
});
|
||||
|
||||
/*
|
||||
* Using the unmarshaller
|
||||
*/
|
||||
CloudEvent<AttributesImpl, Much> myEvent =
|
||||
step
|
||||
.withHeaders(() -> transportHeaders)
|
||||
.withPayload(() -> payload)
|
||||
.unmarshal();
|
||||
```
|
||||
Documentation: https://cloudevents.github.io/sdk-java/api
|
||||
|
|
90
api/pom.xml
90
api/pom.xml
|
@ -1,85 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2018 The CloudEvents Authors
|
||||
~ 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.
|
||||
~
|
||||
-->
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT 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">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>1.2.0</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-api</artifactId>
|
||||
<name>CloudEvents - API</name>
|
||||
<version>1.2.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<module-name>io.cloudevents.api</module-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>${hibernate-validator.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>jakarta.el</artifactId>
|
||||
<version>${jakarta.el.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.10.0</version>
|
||||
<version>${assertj-core.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<jackson.version>2.10.1</jackson.version>
|
||||
<hibernate-validator.version>6.0.17.Final</hibernate-validator.version>
|
||||
<jakarta.el.version>3.0.3</jakarta.el.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* The marker interface for CloudEvents attributes
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public interface Attributes {
|
||||
|
||||
/**
|
||||
* @return Identifies the event. Producers MUST ensure that source + id is unique for each distinct event
|
||||
*/
|
||||
@NotBlank
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* @return A value describing the type of event related to the originating occurrence.
|
||||
*/
|
||||
@NotBlank
|
||||
String getType();
|
||||
|
||||
/**
|
||||
* @return The context in which an event happened.
|
||||
*/
|
||||
@NotNull
|
||||
URI getSource();
|
||||
|
||||
/**
|
||||
* @return The version of the CloudEvents specification which the event uses
|
||||
*/
|
||||
@NotBlank
|
||||
String getSpecversion();
|
||||
|
||||
/**
|
||||
* A common way to get the media type of CloudEvents 'data';
|
||||
* @return If has a value, it MUST follows the <a href="https://tools.ietf.org/html/rfc2046">RFC2046</a>
|
||||
*/
|
||||
@JsonIgnore
|
||||
Optional<String> getMediaType();
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
/*
|
||||
* 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.
|
||||
|
@ -12,34 +12,21 @@
|
|||
* WITHOUT WARRANTIES 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;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import io.cloudevents.lang.Nullable;
|
||||
|
||||
/**
|
||||
* An abstract event envelope
|
||||
* @param <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @author fabiojose
|
||||
* 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>.
|
||||
*/
|
||||
public interface CloudEvent<A extends Attributes, T> {
|
||||
public interface CloudEvent extends CloudEventContext {
|
||||
|
||||
/**
|
||||
* The event context attributes
|
||||
* @return The event data, if any, otherwise {@code null}
|
||||
*/
|
||||
A getAttributes();
|
||||
|
||||
/**
|
||||
* The event data
|
||||
*/
|
||||
Optional<T> getData();
|
||||
|
||||
byte[] getDataBase64();
|
||||
|
||||
/**
|
||||
* The event extensions
|
||||
*/
|
||||
Map<String, Object> getExtensions();
|
||||
@Nullable
|
||||
CloudEventData getData();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import io.cloudevents.lang.Nullable;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.net.URI;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Interface which defines CloudEvent attributes as per specification
|
||||
* <p>
|
||||
* For more info: <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#context-attributes">Context attributes</a>
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface CloudEventAttributes {
|
||||
|
||||
/**
|
||||
* @return The version of the CloudEvents specification which the event uses
|
||||
*/
|
||||
SpecVersion getSpecVersion();
|
||||
|
||||
/**
|
||||
* @return Identifies the event. Producers MUST ensure that source + id is unique for each distinct event
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* @return A value describing the type of event related to the originating occurrence.
|
||||
*/
|
||||
String getType();
|
||||
|
||||
/**
|
||||
* @return The context in which an event happened.
|
||||
*/
|
||||
URI getSource();
|
||||
|
||||
/**
|
||||
* A common way to get the media type of CloudEvents 'data';
|
||||
*
|
||||
* @return If has a value, it MUST follows the <a href="https://tools.ietf.org/html/rfc2046">RFC2046</a>
|
||||
*/
|
||||
@Nullable
|
||||
String getDataContentType();
|
||||
|
||||
/**
|
||||
* @return Return the URI specifying the location of the schema for the provided data
|
||||
*/
|
||||
@Nullable
|
||||
URI getDataSchema();
|
||||
|
||||
/**
|
||||
* @return <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#subject">Subject</a> of the event
|
||||
*/
|
||||
@Nullable
|
||||
String getSubject();
|
||||
|
||||
/**
|
||||
* @return <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#time">Timestamp</a> of when the occurrence happened
|
||||
*/
|
||||
@Nullable
|
||||
OffsetDateTime getTime();
|
||||
|
||||
/**
|
||||
* Get the <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#context-attributes">context attribute</a> named {@code attributeName}
|
||||
*
|
||||
* @param attributeName a valid attribute name
|
||||
* @return the attribute value or null if this instance doesn't contain such attribute
|
||||
* @throws IllegalArgumentException if the provided attribute name it's not a valid attribute for this spec
|
||||
*/
|
||||
@Nullable
|
||||
Object getAttribute(String attributeName) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* @return The non-null <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#context-attributes">context attributes</a> names in this instance
|
||||
*/
|
||||
default Set<String> getAttributeNames() {
|
||||
return getSpecVersion()
|
||||
.getAllAttributes()
|
||||
.stream()
|
||||
.filter(s -> getAttribute(s) != null)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +1,23 @@
|
|||
/**
|
||||
* Copyright 2018 The CloudEvents Authors
|
||||
*
|
||||
/*
|
||||
* 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.beans;
|
||||
package io.cloudevents;
|
||||
|
||||
public class MyCustomEvent {
|
||||
/**
|
||||
* Interface representing an in memory read only representation of CloudEvent attributes and extensions.
|
||||
*/
|
||||
public interface CloudEventContext extends CloudEventAttributes, CloudEventExtensions {
|
||||
}
|
|
@ -1,39 +1,36 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* Interface that defines a wrapper for CloudEvent data.
|
||||
* <p>
|
||||
* This interface can be overridden to include any type of data inside a {@link CloudEvent}, given it has a method to convert back to bytes.
|
||||
*/
|
||||
public interface Builder<A extends Attributes, T> {
|
||||
public interface CloudEventData {
|
||||
|
||||
/**
|
||||
* To build a brand new instance of {@link CloudEvent}
|
||||
* Returns the bytes representation of this data instance.
|
||||
* <p>
|
||||
* Note: depending on the implementation, this operation may be expensive.
|
||||
*
|
||||
* @return this data, represented as bytes.
|
||||
*/
|
||||
CloudEvent<A, T> build();
|
||||
byte[] toBytes();
|
||||
|
||||
/**
|
||||
* To build a brand new instance of {@link CloudEvent} with another
|
||||
* type of 'data'
|
||||
* @param <TT> The new type of 'data'
|
||||
* @param id The new id for the new instance
|
||||
* @param base The base {@link CloudEvent} to copy its attributes
|
||||
* @param newData The new 'data'
|
||||
*/
|
||||
<TT> CloudEvent<A, TT> build(CloudEvent<A, T> base, String id, TT newData);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import io.cloudevents.lang.Nullable;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Materialized CloudEvent extension interface to read/write the extension attributes key/values.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface CloudEventExtension {
|
||||
|
||||
/**
|
||||
* Fill this materialized extension with values from a {@link CloudEventExtensions} implementation.
|
||||
*
|
||||
* @param extensions the extensions where to read from
|
||||
*/
|
||||
void readFrom(CloudEventExtensions extensions);
|
||||
|
||||
/**
|
||||
* 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/URI/OffsetDateTime/byte[]
|
||||
* @throws IllegalArgumentException if the key is unknown to this extension
|
||||
*/
|
||||
@Nullable
|
||||
Object getValue(String key) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Get the set of possible extension attribute keys
|
||||
*
|
||||
* @return set of possible extension attribute keys
|
||||
*/
|
||||
Set<String> getKeys();
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import io.cloudevents.lang.Nullable;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The event extensions.
|
||||
* <p>
|
||||
* Extensions values could be String/Number/Boolean
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface CloudEventExtensions {
|
||||
|
||||
/**
|
||||
* Get the extension attribute named {@code extensionName}
|
||||
*
|
||||
* @param extensionName the extension name
|
||||
* @return the extension value in one of the valid types String/Number/Boolean or null if this instance doesn't contain such extension
|
||||
*/
|
||||
@Nullable
|
||||
Object getExtension(String extensionName);
|
||||
|
||||
/**
|
||||
* @return The non-null extension attributes names in this instance
|
||||
*/
|
||||
Set<String> getExtensionNames();
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import io.cloudevents.rw.CloudEventRWException;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents one of the supported CloudEvents specification versions by this library
|
||||
*/
|
||||
@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"),
|
||||
Arrays.asList("datacontenttype", "dataschema", "subject", "time")
|
||||
);
|
||||
|
||||
private final String stringValue;
|
||||
private final Set<String> mandatoryAttributes;
|
||||
private final Set<String> optionalAttributes;
|
||||
private final Set<String> allAttributes;
|
||||
|
||||
SpecVersion(String stringValue, Collection<String> mandatoryAttributes, Collection<String> optionalAttributes) {
|
||||
this.stringValue = stringValue;
|
||||
this.mandatoryAttributes = Collections.unmodifiableSet(new HashSet<>(mandatoryAttributes));
|
||||
this.optionalAttributes = Collections.unmodifiableSet(new HashSet<>(optionalAttributes));
|
||||
this.allAttributes = Collections.unmodifiableSet(
|
||||
Stream.concat(mandatoryAttributes.stream(), optionalAttributes.stream()).collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.stringValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string as {@link SpecVersion}
|
||||
*
|
||||
* @param sv String representing the spec version
|
||||
* @return The parsed spec version
|
||||
* @throws CloudEventRWException When the spec version string is unrecognized
|
||||
*/
|
||||
public static SpecVersion parse(String sv) {
|
||||
switch (sv) {
|
||||
case "0.3":
|
||||
return SpecVersion.V03;
|
||||
case "1.0":
|
||||
return SpecVersion.V1;
|
||||
default:
|
||||
throw CloudEventRWException.newInvalidSpecVersion(sv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mandatory attributes of the spec version
|
||||
*/
|
||||
public Set<String> getMandatoryAttributes() {
|
||||
return mandatoryAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return optional attributes of the spec version
|
||||
*/
|
||||
public Set<String> getOptionalAttributes() {
|
||||
return optionalAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all attributes for this spec
|
||||
*/
|
||||
public Set<String> getAllAttributes() {
|
||||
return allAttributes;
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
package io.cloudevents.extensions;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
|
||||
public class DistributedTracingExtension {
|
||||
|
||||
private String traceparent;
|
||||
private String tracestate;
|
||||
|
||||
public String getTraceparent() {
|
||||
return traceparent;
|
||||
}
|
||||
|
||||
public void setTraceparent(String traceparent) {
|
||||
this.traceparent = traceparent;
|
||||
}
|
||||
|
||||
public String getTracestate() {
|
||||
return tracestate;
|
||||
}
|
||||
|
||||
public void setTracestate(String tracestate) {
|
||||
this.tracestate = tracestate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DistributedTracingExtension{" +
|
||||
"traceparent='" + traceparent + '\'' +
|
||||
", tracestate='" + tracestate + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
/**
|
||||
* The in-memory format for distributed tracing.
|
||||
* <br/>
|
||||
* Details <a href="https://github.com/cloudevents/spec/blob/v0.2/extensions/distributed-tracing.md#in-memory-formats">here</a>
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public static class Format implements ExtensionFormat {
|
||||
|
||||
public static final String IN_MEMORY_KEY = "distributedTracing";
|
||||
public static final String TRACE_PARENT_KEY = "traceparent";
|
||||
public static final String TRACE_STATE_KEY = "tracestate";
|
||||
|
||||
private final InMemoryFormat memory;
|
||||
private final Map<String, String> transport = new HashMap<>();
|
||||
public Format(DistributedTracingExtension extension) {
|
||||
Objects.requireNonNull(extension);
|
||||
|
||||
memory = InMemoryFormat.of(IN_MEMORY_KEY, extension,
|
||||
DistributedTracingExtension.class);
|
||||
|
||||
transport.put(TRACE_PARENT_KEY, extension.getTraceparent());
|
||||
transport.put(TRACE_STATE_KEY, extension.getTracestate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InMemoryFormat memory() {
|
||||
return memory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transport() {
|
||||
return transport;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshals the {@link DistributedTracingExtension} based on map of extensions.
|
||||
* @param exts
|
||||
* @return
|
||||
*/
|
||||
public static Optional<ExtensionFormat> unmarshall(
|
||||
Map<String, String> exts) {
|
||||
String traceparent = exts.get(Format.TRACE_PARENT_KEY);
|
||||
String tracestate = exts.get(Format.TRACE_STATE_KEY);
|
||||
|
||||
if(null!= traceparent && null!= tracestate) {
|
||||
DistributedTracingExtension dte = new DistributedTracingExtension();
|
||||
dte.setTraceparent(traceparent);
|
||||
dte.setTracestate(tracestate);
|
||||
|
||||
InMemoryFormat inMemory =
|
||||
InMemoryFormat.of(Format.IN_MEMORY_KEY, dte,
|
||||
DistributedTracingExtension.class);
|
||||
|
||||
return Optional.of(
|
||||
ExtensionFormat.of(inMemory,
|
||||
new SimpleEntry<>(Format.TRACE_PARENT_KEY, traceparent),
|
||||
new SimpleEntry<>(Format.TRACE_STATE_KEY, tracestate))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.extensions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Defines a way to add custom extension in the abstract envelop.
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public interface ExtensionFormat {
|
||||
|
||||
/**
|
||||
* The in-memory format to be used by the structured content mode.
|
||||
*/
|
||||
InMemoryFormat memory();
|
||||
|
||||
/**
|
||||
* The transport format to be used by the binary content mode.
|
||||
*/
|
||||
Map<String, String> transport();
|
||||
|
||||
static ExtensionFormat of(final InMemoryFormat inMemory,
|
||||
final String key, final String value) {
|
||||
|
||||
final Map<String, String> transport = new HashMap<>();
|
||||
transport.put(key, value);
|
||||
|
||||
return new ExtensionFormat() {
|
||||
@Override
|
||||
public InMemoryFormat memory() {
|
||||
return inMemory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transport() {
|
||||
return Collections.unmodifiableMap(transport);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
static ExtensionFormat of(final InMemoryFormat inMemory,
|
||||
Entry<String, String> ... transport){
|
||||
Objects.requireNonNull(inMemory);
|
||||
Objects.requireNonNull(transport);
|
||||
|
||||
final Map<String, String> transports = Arrays.asList(transport)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
|
||||
return new ExtensionFormat() {
|
||||
@Override
|
||||
public InMemoryFormat memory() {
|
||||
return inMemory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transport() {
|
||||
return transports;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshals a collection of {@link ExtensionFormat} to {@code Map<String, String>}
|
||||
* @param extensions
|
||||
* @return
|
||||
*/
|
||||
static Map<String, String> marshal(Collection<ExtensionFormat>
|
||||
extensions) {
|
||||
|
||||
return extensions.stream()
|
||||
.map(ExtensionFormat::transport)
|
||||
.flatMap(t -> t.entrySet().stream())
|
||||
.collect(Collectors.toMap(Entry::getKey,
|
||||
Entry::getValue));
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.extensions;
|
||||
|
||||
/**
|
||||
* The in-memory format to be used by the structured content mode.
|
||||
* <br>
|
||||
* See details about in-memory format
|
||||
* <a href="https://github.com/cloudevents/spec/blob/v0.2/documented-extensions.md#usage">here</a>
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public interface InMemoryFormat {
|
||||
|
||||
/**
|
||||
* The in-memory format key
|
||||
*/
|
||||
String getKey();
|
||||
|
||||
/**
|
||||
* The in-memory format value
|
||||
*/
|
||||
Object getValue();
|
||||
|
||||
/**
|
||||
* The type reference for the value. That should be used during the
|
||||
* unmarshal.
|
||||
*/
|
||||
Class<?> getValueType();
|
||||
|
||||
public static InMemoryFormat of(final String key, final Object value,
|
||||
final Class<?> valueType) {
|
||||
return new InMemoryFormat() {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getValueType() {
|
||||
return valueType;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.format.builder.EventStep;
|
||||
import io.cloudevents.format.builder.MarshalStep;
|
||||
import io.cloudevents.fun.AttributeMarshaller;
|
||||
import io.cloudevents.fun.DataMarshaller;
|
||||
import io.cloudevents.fun.ExtensionFormatAccessor;
|
||||
import io.cloudevents.fun.ExtensionMarshaller;
|
||||
import io.cloudevents.fun.FormatHeaderMapper;
|
||||
import io.cloudevents.fun.WireBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public final class BinaryMarshaller {
|
||||
private BinaryMarshaller() {}
|
||||
|
||||
/**
|
||||
* Gets a new builder instance
|
||||
* @param <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
* @param <H> The type of headers value
|
||||
* @return
|
||||
*/
|
||||
public static <A extends Attributes, T, P, H>
|
||||
AttributeMarshalStep<A, T, P, H> builder() {
|
||||
|
||||
return new Builder<A, T, P, H>();
|
||||
}
|
||||
|
||||
public interface AttributeMarshalStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Marshals the {@link Attributes} instance into a
|
||||
* {@code Map<String, String>}
|
||||
* @param marshaller
|
||||
* @return
|
||||
*/
|
||||
ExtensionsAccessorStep<A, T, P, H> map(AttributeMarshaller<A> marshaller);
|
||||
}
|
||||
|
||||
public interface ExtensionsAccessorStep<A extends Attributes, T, P, H> {
|
||||
|
||||
/**
|
||||
* To get access of internal collection of {@link ExtensionFormat}
|
||||
* @param accessor
|
||||
* @return
|
||||
*/
|
||||
ExtensionsStep<A, T, P, H> map(ExtensionFormatAccessor<A, T> accessor);
|
||||
|
||||
}
|
||||
|
||||
public interface ExtensionsStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Marshals the collection of {@link ExtensionFormat} into a
|
||||
* {@code Map<String, String>}
|
||||
* @param marshaller
|
||||
* @return
|
||||
*/
|
||||
HeaderMapStep<A, T, P, H> map(ExtensionMarshaller marshaller);
|
||||
}
|
||||
|
||||
public interface HeaderMapStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Marshals the map of attributes and extensions into a map of headers
|
||||
* @param mapper
|
||||
* @return
|
||||
*/
|
||||
DataMarshallerStep<A, T, P, H> map(FormatHeaderMapper<H> mapper);
|
||||
}
|
||||
|
||||
public interface DataMarshallerStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Marshals the 'data' into payload
|
||||
* @param marshaller
|
||||
* @return
|
||||
*/
|
||||
BuilderStep<A, T, P, H> map(DataMarshaller<P, T, H> marshaller);
|
||||
}
|
||||
|
||||
public interface BuilderStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Builds the {@link Wire} to use for wire transfer
|
||||
* @param builder
|
||||
* @return
|
||||
*/
|
||||
EventStep<A, T, P, H> builder(WireBuilder<P, String, H> builder);
|
||||
}
|
||||
|
||||
private static final class Builder<A extends Attributes, T, P, H> implements
|
||||
AttributeMarshalStep<A, T, P, H>,
|
||||
ExtensionsAccessorStep<A, T, P, H>,
|
||||
ExtensionsStep<A, T, P, H>,
|
||||
DataMarshallerStep<A, T, P, H>,
|
||||
HeaderMapStep<A, T, P, H>,
|
||||
BuilderStep<A, T, P, H>,
|
||||
EventStep<A, T, P, H>,
|
||||
MarshalStep<P, H> {
|
||||
|
||||
private AttributeMarshaller<A> attributeMarshaller;
|
||||
private ExtensionFormatAccessor<A, T> extensionsAccessor;
|
||||
private ExtensionMarshaller extensionMarshaller;
|
||||
private FormatHeaderMapper<H> headerMapper;
|
||||
private DataMarshaller<P, T, H> dataMarshaller;
|
||||
private WireBuilder<P, String, H> wireBuilder;
|
||||
private Supplier<CloudEvent<A, T>> eventSupplier;
|
||||
|
||||
@Override
|
||||
public ExtensionsAccessorStep<A, T, P, H> map(AttributeMarshaller<A> marshaller) {
|
||||
this.attributeMarshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionsStep<A, T, P, H> map(ExtensionFormatAccessor<A, T> accessor) {
|
||||
this.extensionsAccessor = accessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderMapStep<A, T, P, H> map(ExtensionMarshaller marshaller) {
|
||||
this.extensionMarshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataMarshallerStep<A, T, P, H> map(FormatHeaderMapper<H> mapper) {
|
||||
this.headerMapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderStep<A, T, P, H> map(DataMarshaller<P, T, H> marshaller) {
|
||||
this.dataMarshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventStep<A, T, P, H> builder(WireBuilder<P, String, H> builder) {
|
||||
this.wireBuilder = builder;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarshalStep<P, H> withEvent(Supplier<CloudEvent<A, T>> event) {
|
||||
this.eventSupplier = event;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wire<P, String, H> marshal() {
|
||||
CloudEvent<A, T> event = eventSupplier.get();
|
||||
|
||||
Map<String, String> attributesMap =
|
||||
attributeMarshaller.marshal(event.getAttributes());
|
||||
|
||||
Collection<ExtensionFormat> extensionsFormat =
|
||||
extensionsAccessor.extensionsOf(event);
|
||||
|
||||
Map<String, String> extensionsMap =
|
||||
extensionMarshaller.marshal(extensionsFormat);
|
||||
|
||||
Map<String, H> headers =
|
||||
headerMapper.map(attributesMap, extensionsMap);
|
||||
|
||||
P payload = null;
|
||||
if(event.getData().isPresent()) {
|
||||
payload = dataMarshaller.marshal(event.getData().get(),
|
||||
headers);
|
||||
}
|
||||
|
||||
return wireBuilder.build(payload, headers);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.format.builder.HeadersStep;
|
||||
import io.cloudevents.format.builder.PayloadStep;
|
||||
import io.cloudevents.format.builder.UnmarshalStep;
|
||||
import io.cloudevents.fun.AttributeUnmarshaller;
|
||||
import io.cloudevents.fun.BinaryFormatAttributeMapper;
|
||||
import io.cloudevents.fun.DataUnmarshaller;
|
||||
import io.cloudevents.fun.EventBuilder;
|
||||
import io.cloudevents.fun.ExtensionUmarshaller;
|
||||
import io.cloudevents.fun.FormatExtensionMapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public final class BinaryUnmarshaller {
|
||||
private BinaryUnmarshaller() {}
|
||||
|
||||
/**
|
||||
* Gets a new builder instance
|
||||
* @param <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
* @return
|
||||
*/
|
||||
public static <A extends Attributes, T, P> AttributeMapStep<A, T, P>
|
||||
builder() {
|
||||
return new Builder<A, T, P>();
|
||||
}
|
||||
|
||||
public interface AttributeMapStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Maps the map of headers into map of attributes
|
||||
* @param unmarshaller
|
||||
* @return
|
||||
*/
|
||||
AttributeUmarshallStep<A, T, P> map(BinaryFormatAttributeMapper unmarshaller);
|
||||
}
|
||||
|
||||
public interface AttributeUmarshallStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Unmarshals the map of attributes into instance of {@link Attributes}
|
||||
* @param unmarshaller
|
||||
* @return
|
||||
*/
|
||||
DataUnmarshallerStep<A, T, P> map(AttributeUnmarshaller<A> unmarshaller);
|
||||
}
|
||||
|
||||
public interface DataUnmarshallerStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Unmarshals the payload into actual 'data' type
|
||||
* @param unmarshaller
|
||||
* @return
|
||||
*/
|
||||
DataUnmarshallerStep<A, T, P> map(String mime, DataUnmarshaller<P, T, A> unmarshaller);
|
||||
|
||||
ExtensionsMapStep<A, T, P> next();
|
||||
}
|
||||
|
||||
public interface ExtensionsMapStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Maps the headers map into map of extensions
|
||||
* @param mapper
|
||||
* @return
|
||||
*/
|
||||
ExtensionsStep<A, T, P> map(FormatExtensionMapper mapper);
|
||||
}
|
||||
|
||||
public interface ExtensionsStepBegin<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Starts the configuration for extensions unmarshal
|
||||
* @return
|
||||
*/
|
||||
ExtensionsStep<A, T, P> beginExtensions();
|
||||
}
|
||||
|
||||
public interface ExtensionsStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Unmarshals a extension, based on the map of extensions.
|
||||
*
|
||||
* <br>
|
||||
* <br>
|
||||
* This is an optional step, because you do not have extensions or
|
||||
* do not want to process them at all.
|
||||
*
|
||||
* @param unmarshaller
|
||||
* @return
|
||||
*/
|
||||
ExtensionsStep<A, T, P> map(ExtensionUmarshaller unmarshaller);
|
||||
|
||||
/**
|
||||
* Ends the configuration for extensions unmarshal
|
||||
* @return
|
||||
*/
|
||||
BuilderStep<A, T, P> next();
|
||||
}
|
||||
|
||||
public interface BuilderStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Takes the builder to build {@link CloudEvent} instances
|
||||
* @param builder
|
||||
* @return
|
||||
*/
|
||||
HeadersStep<A, T, P> builder(EventBuilder<T, A> builder);
|
||||
}
|
||||
|
||||
private static final class Builder<A extends Attributes, T, P> implements
|
||||
AttributeMapStep<A, T, P>,
|
||||
AttributeUmarshallStep<A, T, P>,
|
||||
DataUnmarshallerStep<A, T, P>,
|
||||
ExtensionsMapStep<A, T, P>,
|
||||
ExtensionsStep<A, T, P>,
|
||||
BuilderStep<A, T, P>,
|
||||
HeadersStep<A, T, P>,
|
||||
PayloadStep<A, T, P>,
|
||||
UnmarshalStep<A, T>{
|
||||
|
||||
private BinaryFormatAttributeMapper attributeMapper;
|
||||
private AttributeUnmarshaller<A> attributeUnmarshaller;
|
||||
private Map<String, DataUnmarshaller<P, T, A>> dataUnmarshallers =
|
||||
new HashMap<>();
|
||||
private FormatExtensionMapper extensionMapper;
|
||||
private Set<ExtensionUmarshaller> extensionUnmarshallers =
|
||||
new HashSet<>();
|
||||
private EventBuilder<T, A> eventBuilder;
|
||||
private Supplier<Map<String, Object>> headersSupplier;
|
||||
private Supplier<P> payloadSupplier;
|
||||
|
||||
@Override
|
||||
public AttributeUmarshallStep<A, T, P> map(BinaryFormatAttributeMapper mapper) {
|
||||
this.attributeMapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataUnmarshallerStep<A, T, P> map(AttributeUnmarshaller<A> unmarshaller) {
|
||||
this.attributeUnmarshaller = unmarshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataUnmarshallerStep<A, T, P> map(String mime, DataUnmarshaller<P, T, A> unmarshaller) {
|
||||
this.dataUnmarshallers.put(mime, unmarshaller);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A, T, P> next() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionsStep<A, T, P> map(FormatExtensionMapper mapper) {
|
||||
this.extensionMapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionsStep<A, T, P> map(ExtensionUmarshaller unmarshaller) {
|
||||
this.extensionUnmarshallers.add(unmarshaller);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeadersStep<A, T, P> builder(EventBuilder<T, A> builder) {
|
||||
this.eventBuilder = builder;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayloadStep<A, T, P> withHeaders(
|
||||
Supplier<Map<String, Object>> headers) {
|
||||
this.headersSupplier = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnmarshalStep<A, T> withPayload(Supplier<P> payload) {
|
||||
this.payloadSupplier = payload;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent<A, T> unmarshal() {
|
||||
|
||||
Map<String, Object> headers = headersSupplier.get();
|
||||
P payload = payloadSupplier.get();
|
||||
|
||||
Map<String, String> attributesMap = attributeMapper.map(headers);
|
||||
|
||||
A attributes = attributeUnmarshaller.unmarshal(attributesMap);
|
||||
|
||||
T data = attributes.getMediaType()
|
||||
.map((mime) -> dataUnmarshallers.get(mime))
|
||||
.filter(Objects::nonNull)
|
||||
.map(unmarshaller ->
|
||||
unmarshaller.unmarshal(payload, attributes))
|
||||
.orElse(null);
|
||||
|
||||
final Map<String, String> extensionsMap =
|
||||
extensionMapper.map(headers);
|
||||
|
||||
List<ExtensionFormat> extensions =
|
||||
extensionUnmarshallers.stream()
|
||||
.map(unmarshaller ->
|
||||
unmarshaller.unmarshal(extensionsMap))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return eventBuilder.build(data, attributes, extensions);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,185 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.format.builder.EventStep;
|
||||
import io.cloudevents.format.builder.MarshalStep;
|
||||
import io.cloudevents.fun.EnvelopeMarshaller;
|
||||
import io.cloudevents.fun.ExtensionFormatAccessor;
|
||||
import io.cloudevents.fun.ExtensionMarshaller;
|
||||
import io.cloudevents.fun.FormatHeaderMapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public class StructuredMarshaller {
|
||||
StructuredMarshaller() {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <A> The attributes type
|
||||
* @param <T> The CloudEvents 'data' type
|
||||
* @param <P> The CloudEvents marshaled envelope type
|
||||
* @param <H> The header type
|
||||
* @return A new builder to build structured mashaller
|
||||
*/
|
||||
public static <A extends Attributes, T, P, H> MediaTypeStep<A, T, P, H>
|
||||
builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
public interface MediaTypeStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Sets the media type of CloudEvents envelope
|
||||
* @param headerName Example {@code Content-Type} for HTTP
|
||||
* @param mediaType Example: {@code application/cloudevents+json}
|
||||
*/
|
||||
EnvelopeMarshallerStep<A, T, P, H> mime(String headerName, H mediaType);
|
||||
}
|
||||
|
||||
public interface EnvelopeMarshallerStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Sets the marshaller for the CloudEvent
|
||||
* @param marshaller
|
||||
*/
|
||||
ExtensionAccessorStep<A, T, P, H> map(EnvelopeMarshaller<A, T, P> marshaller);
|
||||
}
|
||||
|
||||
public interface ExtensionAccessorStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* To skip the extension special handling
|
||||
*/
|
||||
EventStep<A, T, P, H> skip();
|
||||
ExtensionMarshallerStep<A, T, P, H> map(ExtensionFormatAccessor<A, T> accessor);
|
||||
}
|
||||
|
||||
public interface ExtensionMarshallerStep<A extends Attributes, T, P, H> {
|
||||
HeaderMapperStep<A, T, P, H> map(ExtensionMarshaller marshaller);
|
||||
}
|
||||
|
||||
public interface HeaderMapperStep<A extends Attributes, T, P, H> {
|
||||
EventStep<A, T, P, H> map(FormatHeaderMapper<H> mapper);
|
||||
}
|
||||
|
||||
private static final class Builder<A extends Attributes, T, P, H> implements
|
||||
MediaTypeStep<A, T, P, H>,
|
||||
EnvelopeMarshallerStep<A, T, P, H>,
|
||||
ExtensionAccessorStep<A, T, P, H>,
|
||||
ExtensionMarshallerStep<A, T, P, H>,
|
||||
HeaderMapperStep<A, T, P, H>,
|
||||
EventStep<A, T, P, H>,
|
||||
MarshalStep<P, H>{
|
||||
|
||||
private static final Map<String, String> NO_ATTRS =
|
||||
new HashMap<>();
|
||||
|
||||
private String headerName;
|
||||
private H mediaType;
|
||||
|
||||
private EnvelopeMarshaller<A, T, P> marshaller;
|
||||
|
||||
private ExtensionFormatAccessor<A, T> extensionAccessor;
|
||||
|
||||
private ExtensionMarshaller extensionMarshaller;
|
||||
|
||||
private FormatHeaderMapper<H> headerMapper;
|
||||
|
||||
private Supplier<CloudEvent<A, T>> event;
|
||||
|
||||
@Override
|
||||
public EnvelopeMarshallerStep<A, T, P, H> mime(String headerName, H mediaType) {
|
||||
Objects.requireNonNull(headerName);
|
||||
Objects.requireNonNull(mediaType);
|
||||
|
||||
this.headerName = headerName;
|
||||
this.mediaType = mediaType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionAccessorStep<A, T, P, H> map(EnvelopeMarshaller<A, T, P> marshaller) {
|
||||
Objects.requireNonNull(marshaller);
|
||||
|
||||
this.marshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventStep<A, T, P, H> skip() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionMarshallerStep<A, T, P, H> map(ExtensionFormatAccessor<A, T> accessor) {
|
||||
Objects.requireNonNull(accessor);
|
||||
|
||||
this.extensionAccessor = accessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderMapperStep<A, T, P, H> map(ExtensionMarshaller marshaller) {
|
||||
Objects.requireNonNull(marshaller);
|
||||
|
||||
this.extensionMarshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventStep<A, T, P, H> map(FormatHeaderMapper<H> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
|
||||
this.headerMapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarshalStep<P, H> withEvent(Supplier<CloudEvent<A, T>> event) {
|
||||
Objects.requireNonNull(event);
|
||||
|
||||
this.event = event;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wire<P, String, H> marshal() {
|
||||
CloudEvent<A, T> ce = event.get();
|
||||
|
||||
P payload = marshaller.marshal(ce);
|
||||
|
||||
Map<String, H> headers =
|
||||
Optional.ofNullable(extensionAccessor)
|
||||
.map(accessor -> accessor.extensionsOf(ce))
|
||||
.map(extensions -> extensionMarshaller.marshal(extensions))
|
||||
.map(extensions -> headerMapper.map(NO_ATTRS, extensions))
|
||||
.orElse(new HashMap<>());
|
||||
|
||||
headers.put(headerName, mediaType);
|
||||
|
||||
return new Wire<>(payload, headers);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.format.builder.HeadersStep;
|
||||
import io.cloudevents.format.builder.PayloadStep;
|
||||
import io.cloudevents.format.builder.UnmarshalStep;
|
||||
import io.cloudevents.fun.EnvelopeUnmarshaller;
|
||||
import io.cloudevents.fun.ExtensionUmarshaller;
|
||||
import io.cloudevents.fun.FormatExtensionMapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public class StructuredUnmarshaller {
|
||||
StructuredUnmarshaller() {}
|
||||
|
||||
public static <A extends Attributes, T, P> ExtensionMapperStep<A, T, P>
|
||||
builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
public static interface ExtensionMapperStep<A extends Attributes, T, P> {
|
||||
EnvelopeUnmarshallerStep<A, T, P> skip();
|
||||
ExtensionUnmarshallerStep<A, T, P> map(FormatExtensionMapper mapper);
|
||||
}
|
||||
|
||||
public static interface ExtensionUnmarshallerStep<A extends Attributes, T, P> {
|
||||
ExtensionUnmarshallerStep<A, T, P> map(ExtensionUmarshaller unmarshaller);
|
||||
EnvelopeUnmarshallerStep<A, T, P> next();
|
||||
}
|
||||
|
||||
public static interface EnvelopeUnmarshallerStep<A extends Attributes, T, P> {
|
||||
HeadersStep<A, T, P> map(EnvelopeUnmarshaller<A, T, P> unmarshaller);
|
||||
}
|
||||
|
||||
private static final class Builder<A extends Attributes, T, P> implements
|
||||
ExtensionMapperStep<A, T, P>,
|
||||
ExtensionUnmarshallerStep<A, T, P>,
|
||||
EnvelopeUnmarshallerStep<A, T, P>,
|
||||
HeadersStep<A, T, P>,
|
||||
PayloadStep<A, T, P>,
|
||||
UnmarshalStep<A, T>{
|
||||
|
||||
private FormatExtensionMapper extensionMapper;
|
||||
|
||||
private Set<ExtensionUmarshaller> extensionUnmarshallers =
|
||||
new HashSet<>();
|
||||
|
||||
private EnvelopeUnmarshaller<A, T, P> unmarshaller;
|
||||
|
||||
private Supplier<Map<String, Object>> headersSupplier;
|
||||
|
||||
private Supplier<P> payloadSupplier;
|
||||
|
||||
@Override
|
||||
public Builder<A, T, P> next() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnvelopeUnmarshallerStep<A, T, P> skip() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionUnmarshallerStep<A, T, P> map(FormatExtensionMapper mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
|
||||
this.extensionMapper = mapper;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionUnmarshallerStep<A, T, P> map(ExtensionUmarshaller unmarshaller) {
|
||||
Objects.requireNonNull(unmarshaller);
|
||||
|
||||
this.extensionUnmarshallers.add(unmarshaller);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeadersStep<A, T, P> map(EnvelopeUnmarshaller<A, T, P> unmarshaller) {
|
||||
Objects.requireNonNull(unmarshaller);
|
||||
|
||||
this.unmarshaller = unmarshaller;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayloadStep<A, T, P> withHeaders(Supplier<Map<String, Object>> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
this.headersSupplier = headers;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnmarshalStep<A, T> withPayload(Supplier<P> payload) {
|
||||
Objects.requireNonNull(payload);
|
||||
|
||||
this.payloadSupplier = payload;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent<A, T> unmarshal() {
|
||||
|
||||
Map<String, Object> headers = headersSupplier.get();
|
||||
P payload = payloadSupplier.get();
|
||||
|
||||
final Map<String, String> extensionsMap =
|
||||
Optional.ofNullable(extensionMapper)
|
||||
.map(mapper -> mapper.map(headers))
|
||||
.orElse(new HashMap<>());
|
||||
|
||||
return unmarshaller.unmarshal(payload,
|
||||
() ->
|
||||
extensionUnmarshallers.stream()
|
||||
.map(unmarshaller ->
|
||||
unmarshaller.unmarshal(extensionsMap))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a result of binary marshal, to be used by the wire transfer
|
||||
*
|
||||
* @author fabiojose
|
||||
* @param <T> The payload type
|
||||
* @param <K> The header key type
|
||||
* @param <V> The header value type
|
||||
*/
|
||||
public class Wire<T, K, V> {
|
||||
|
||||
private final T payload;
|
||||
private final Map<K, V> headers;
|
||||
|
||||
public Wire(T payload, Map<K, V> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
this.payload = payload;
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* The payload
|
||||
*/
|
||||
public Optional<T> getPayload() {
|
||||
return Optional.ofNullable(payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* The headers
|
||||
*/
|
||||
public Map<K, V> getHeaders() {
|
||||
return Collections.unmodifiableMap(headers);
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format.builder;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* @param <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
* @param <H> The headers value type
|
||||
*/
|
||||
public interface EventStep<A extends Attributes, T, P, H> {
|
||||
|
||||
/**
|
||||
* Supplies the {@link CloudEvent} instance which will be marshaled
|
||||
* @param event cloud event to marshal
|
||||
* @return The next step of builder
|
||||
*/
|
||||
MarshalStep<P, H> withEvent(Supplier<CloudEvent<A, T>> event);
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format.builder;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* @param <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
*/
|
||||
public interface HeadersStep<A extends Attributes, T, P> {
|
||||
|
||||
/**
|
||||
* Supplies a map of headers to be used by the unmarshaller
|
||||
* @param headers
|
||||
* @return The next step of builder
|
||||
*/
|
||||
PayloadStep<A, T, P> withHeaders(Supplier<Map<String, Object>> headers);
|
||||
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format.builder;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.format.Wire;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* @param <P> The payload type
|
||||
* @param <H> The headers value type
|
||||
*/
|
||||
public interface MarshalStep<P, H> {
|
||||
|
||||
/**
|
||||
* Marshals the {@link CloudEvent} instance as {@link Wire}
|
||||
* @return The wire to use in the transports bindings
|
||||
*/
|
||||
Wire<P, String, H> marshal();
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format.builder;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* @param <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
*/
|
||||
public interface PayloadStep<A extends Attributes, T, P> {
|
||||
|
||||
/**
|
||||
* Supplies the payload that will be used by the unmarshaller
|
||||
* @param payload
|
||||
* @return The next step o builder
|
||||
*/
|
||||
UnmarshalStep<A, T> withPayload(Supplier<P> payload);
|
||||
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.format.builder;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* @param <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
public interface UnmarshalStep<A extends Attributes, T> {
|
||||
|
||||
/**
|
||||
* Unmarshals the payload and headers to {@link CloudEvent} instance
|
||||
* @return New cloud event instance
|
||||
*/
|
||||
CloudEvent<A, T> unmarshal();
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.fun;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* @param <P> The payload type
|
||||
* @param <T> The 'data' type
|
||||
* @param <H> The type of headers value
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DataMarshaller<P, T, H> {
|
||||
|
||||
/**
|
||||
* Marshals the 'data' into payload
|
||||
* @param data
|
||||
* @param headers
|
||||
* @return
|
||||
* @throws RuntimeException When something bad happens during the marshal process
|
||||
*/
|
||||
P marshal(T data, Map<String, H> headers) throws RuntimeException;
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.fun;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
|
||||
/**
|
||||
* For the 'data' unmarshalling.
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* @param <P> The payload type
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DataUnmarshaller<P, T, A extends Attributes> {
|
||||
|
||||
/**
|
||||
* Unmarshals the payload into 'data'
|
||||
* @param payload
|
||||
* @param attributes
|
||||
* @return
|
||||
* @throws RuntimeException If something bad happens during the umarshal
|
||||
*/
|
||||
T unmarshal(P payload, A attributes);
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.fun;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public interface EnvelopeUnmarshaller<A extends Attributes, T, P> {
|
||||
|
||||
/**
|
||||
* Unmarshals the payload into {@link CloudEvent} instance implementation
|
||||
*
|
||||
* @param payload The envelope payload
|
||||
* @param extensions Supplies a list of {@link ExtensionFormat}
|
||||
* @return The unmarshalled impl of CloudEvent
|
||||
*/
|
||||
CloudEvent<A, T> unmarshal(P payload,
|
||||
Supplier<List<ExtensionFormat>> extensions);
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.fun;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
|
||||
/**
|
||||
* To build the event.
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface EventBuilder<T, A extends Attributes> {
|
||||
|
||||
/**
|
||||
* Builds a new event using 'data', 'attributes' and 'extensions'
|
||||
*/
|
||||
CloudEvent<A, T> build(T data, A attributes,
|
||||
Collection<ExtensionFormat> extensions);
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package io.cloudevents.fun;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ExtensionFormatAccessor<A extends Attributes, T> {
|
||||
|
||||
/**
|
||||
* To get access to the internal collection of {@link ExtensionFormat} inside
|
||||
* the {@link CloudEvent} implementation
|
||||
*
|
||||
* @param cloudEvent
|
||||
* @return
|
||||
*/
|
||||
Collection<ExtensionFormat> extensionsOf(CloudEvent<A, T> cloudEvent);
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.fun;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ExtensionMarshaller {
|
||||
|
||||
/**
|
||||
* Marshals a collections of {@link ExtensionFormat} into map
|
||||
* @param extensions
|
||||
* @return
|
||||
*/
|
||||
Map<String, String> marshal(Collection<ExtensionFormat> extensions);
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.fun;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ExtensionUmarshaller {
|
||||
|
||||
/**
|
||||
* Unmarshals the map of extensions into {@link ExtensionFormat}
|
||||
* @param extensions
|
||||
* @return
|
||||
*/
|
||||
Optional<ExtensionFormat> unmarshal(Map<String, String> extensions);
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.fun;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FormatExtensionMapper {
|
||||
|
||||
/**
|
||||
* Maps the 'headers' of binary format into extensions
|
||||
* @param headers
|
||||
*/
|
||||
Map<String, String> map(Map<String, Object> headers);
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.fun;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @param <H> The header value type
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FormatHeaderMapper<H> {
|
||||
|
||||
/**
|
||||
* Maps the 'attributes' and 'extensions' of CloudEvent envelop to
|
||||
* 'headers' of binary format
|
||||
* @param attributes
|
||||
* @return
|
||||
*/
|
||||
Map<String, H> map(Map<String, String> attributes,
|
||||
Map<String, String> extensions);
|
||||
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.json;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.fun.DataMarshaller;
|
||||
import io.cloudevents.fun.DataUnmarshaller;
|
||||
|
||||
public final class Json {
|
||||
|
||||
public static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
static {
|
||||
// add Jackson datatype for ZonedDateTime
|
||||
MAPPER.registerModule(new Jdk8Module());
|
||||
|
||||
final SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer());
|
||||
module.addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer());
|
||||
MAPPER.registerModule(module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a POJO to JSON using the underlying Jackson mapper.
|
||||
*
|
||||
* @param obj a POJO
|
||||
* @return a String containing the JSON representation of the given POJO.
|
||||
* @throws IllegalStateException if a property cannot be encoded.
|
||||
*/
|
||||
public static String encode(final Object obj) throws IllegalStateException {
|
||||
try {
|
||||
return MAPPER.writeValueAsString(obj);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a POJO to JSON using the underlying Jackson mapper.
|
||||
*
|
||||
* @param obj a POJO
|
||||
* @return a byte array containing the JSON representation of the given POJO.
|
||||
* @throws IllegalStateException if a property cannot be encoded.
|
||||
*/
|
||||
public static byte[] binaryEncode(final Object obj) throws IllegalStateException {
|
||||
try {
|
||||
return MAPPER.writeValueAsBytes(obj);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T fromInputStream(final InputStream inputStream,
|
||||
Class<T> clazz) {
|
||||
try {
|
||||
return MAPPER.readValue(inputStream, clazz);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to encode as JSON: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T fromInputStream(final InputStream inputStream,
|
||||
final TypeReference<T> type) {
|
||||
try {
|
||||
return MAPPER.readValue(inputStream, type);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to encode as JSON: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a given JSON string to a POJO of the given class type.
|
||||
*
|
||||
* @param str the JSON string.
|
||||
* @param clazz the class to map to.
|
||||
* @param <T> the generic type.
|
||||
* @return an instance of T or {@code null} when {@code str} is an empty string or {@code null}
|
||||
* @throws IllegalStateException when there is a parsing or invalid mapping.
|
||||
*/
|
||||
protected static <T> T decodeValue(final String str, final Class<T> clazz) throws IllegalStateException {
|
||||
|
||||
if(null!= str && !"".equals(str.trim())) {
|
||||
try {
|
||||
return MAPPER.readValue(str.trim(), clazz);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to decode: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static <T> T binaryDecodeValue(byte[] payload, final Class<T> clazz) {
|
||||
if(null!= payload) {
|
||||
try {
|
||||
return MAPPER.readValue(payload, clazz);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to decode: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a given JSON string to a POJO of the given type.
|
||||
*
|
||||
* @param str the JSON string.
|
||||
* @param type the type to map to.
|
||||
* @param <T> the generic type.
|
||||
* @return an instance of T or {@code null} when {@code str} is an empty string or {@code null}
|
||||
* @throws IllegalStateException when there is a parsing or invalid mapping.
|
||||
*/
|
||||
public static <T> T decodeValue(final String str, final TypeReference<T> type) throws IllegalStateException {
|
||||
if(null!= str && !"".equals(str.trim())) {
|
||||
try {
|
||||
return MAPPER.readValue(str.trim(), type);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to decode: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example of use:
|
||||
* <pre>
|
||||
* String someJson = "...";
|
||||
* Class<?> clazz = Much.class;
|
||||
*
|
||||
* Json.decodeValue(someJson, CloudEventImpl.class, clazz);
|
||||
* </pre>
|
||||
* @param str The JSON String to parse
|
||||
* @param parametrized Actual full type
|
||||
* @param parameterClasses Type parameters to apply
|
||||
* @param <T> the generic type.
|
||||
* @return An instance of T or {@code null} when {@code str} is an empty string or {@code null}
|
||||
* @see ObjectMapper#getTypeFactory
|
||||
* @see TypeFactory#constructParametricType(Class, Class...)
|
||||
*/
|
||||
public static <T> T decodeValue(final String str, Class<?> parametrized,
|
||||
Class<?>...parameterClasses) {
|
||||
if(null!= str && !"".equals(str.trim())) {
|
||||
try {
|
||||
JavaType type =
|
||||
MAPPER.getTypeFactory()
|
||||
.constructParametricType(parametrized,
|
||||
parameterClasses);
|
||||
|
||||
return MAPPER.readValue(str.trim(), type);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to decode: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example of use:
|
||||
* <pre>
|
||||
* String someJson = "...";
|
||||
* Class<?> clazz = Much.class;
|
||||
*
|
||||
* Json.decodeValue(someJson, CloudEventImpl.class, clazz);
|
||||
* </pre>
|
||||
* @param json The JSON byte array to parse
|
||||
* @param parametrized Actual full type
|
||||
* @param parameterClasses Type parameters to apply
|
||||
* @param <T> the generic type.
|
||||
* @return An instance of T or {@code null} when {@code str} is an empty string or {@code null}
|
||||
* @see ObjectMapper#getTypeFactory
|
||||
* @see TypeFactory#constructParametricType(Class, Class...)
|
||||
*/
|
||||
public static <T> T binaryDecodeValue(final byte[] json, Class<?> parametrized,
|
||||
Class<?>...parameterClasses) {
|
||||
if(null!= json) {
|
||||
try {
|
||||
JavaType type =
|
||||
MAPPER.getTypeFactory()
|
||||
.constructParametricType(parametrized,
|
||||
parameterClasses);
|
||||
|
||||
return MAPPER.readValue(json, type);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to decode: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON Data Unmarshaller
|
||||
* @param <T> The 'data' type
|
||||
* @param <A> The attributes type
|
||||
* @param type The type of 'data'
|
||||
* @return A new instance of {@link DataUnmarshaller}
|
||||
*/
|
||||
public static <T, A extends Attributes> DataUnmarshaller<String, T, A>
|
||||
umarshaller(Class<T> type) {
|
||||
return (payload, attributes) -> Json.decodeValue(payload, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshals a byte array into T type
|
||||
* @param <T> The 'data' type
|
||||
* @param <A> The attributes type
|
||||
* @return The data objects
|
||||
*/
|
||||
public static <T, A extends Attributes> DataUnmarshaller<byte[], T, A>
|
||||
binaryUmarshaller(Class<T> type) {
|
||||
return (payload, attributes) -> Json.binaryDecodeValue(payload, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON Data Marshaller that produces a {@link String}
|
||||
* @param <T> The 'data' type
|
||||
* @param <H> The type of headers value
|
||||
* @return A new instance of {@link DataMarshaller}
|
||||
*/
|
||||
public static <T, H> DataMarshaller<String, T, H> marshaller() {
|
||||
return (data, headers) -> Json.encode(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshalls the 'data' value as JSON, producing a byte array
|
||||
* @param <T> The 'data' type
|
||||
* @param <H> The type of headers value
|
||||
* @param data The 'data' value
|
||||
* @param headers The headers
|
||||
* @return A byte array with 'data' value encoded JSON
|
||||
*/
|
||||
public static <T, H> byte[] binaryMarshal(T data,
|
||||
Map<String, H> headers) {
|
||||
return Json.binaryEncode(data);
|
||||
}
|
||||
|
||||
private Json() {
|
||||
// no-op
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
public class ZonedDateTimeDeserializer extends StdDeserializer<ZonedDateTime> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ZonedDateTimeDeserializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public ZonedDateTimeDeserializer(Class<?> vc) {
|
||||
super(vc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZonedDateTime deserialize(JsonParser jsonparser, DeserializationContext ctxt) throws IOException {
|
||||
// not serializing timezone data yet
|
||||
try {
|
||||
return ZonedDateTime.parse(jsonparser.getText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
|
||||
} catch (DateTimeException e) {
|
||||
throw new IllegalArgumentException("could not parse");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
public class ZonedDateTimeSerializer extends StdSerializer<ZonedDateTime> {
|
||||
|
||||
private static final long serialVersionUID = 6245182835980474796L;
|
||||
|
||||
public ZonedDateTimeSerializer() {
|
||||
this(null, false);
|
||||
}
|
||||
|
||||
protected ZonedDateTimeSerializer(Class<?> t, boolean dummy) {
|
||||
super(t, dummy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ZonedDateTime time, JsonGenerator generator,
|
||||
SerializerProvider provider) throws IOException {
|
||||
|
||||
generator.writeString(time.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.lang;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.meta.TypeQualifierNickname;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
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
|
||||
@Nonnull(when = MAYBE)
|
||||
@TypeQualifierNickname
|
||||
public @interface Nullable {
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Represents an object that can be read as CloudEvent context attributes and extensions.
|
||||
* <p>
|
||||
* An object (in particular, buffered objects) can implement both this interface and {@link CloudEventReader}.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface CloudEventContextReader {
|
||||
|
||||
/**
|
||||
* Read the context attributes and extensions using the provided writer
|
||||
*
|
||||
* @param writer context writer
|
||||
* @throws CloudEventRWException if something went wrong during the read.
|
||||
*/
|
||||
void readContext(CloudEventContextWriter writer) throws CloudEventRWException;
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.CloudEventData;
|
||||
|
||||
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
|
||||
public interface CloudEventDataMapper<R extends CloudEventData> {
|
||||
|
||||
/**
|
||||
* Map {@code data} to another {@link CloudEventData} instance.
|
||||
*
|
||||
* @param data the input data
|
||||
* @return The new data
|
||||
* @throws CloudEventRWException is anything goes wrong while mapping the input data
|
||||
*/
|
||||
R map(CloudEventData data) throws CloudEventRWException;
|
||||
|
||||
/**
|
||||
* @return No-op identity mapper which can be used as default when no mapper is provided.
|
||||
*/
|
||||
static CloudEventDataMapper<CloudEventData> identity() {
|
||||
return d -> d;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* 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.lang.Nullable;
|
||||
|
||||
/**
|
||||
* This class is the exception Protocol Binding and Event Format implementers can use to signal errors while serializing/deserializing CloudEvent.
|
||||
*/
|
||||
public class CloudEventRWException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* The kind of error that happened while serializing/deserializing
|
||||
*/
|
||||
public enum CloudEventRWExceptionKind {
|
||||
/**
|
||||
* Spec version string is not recognized by this particular SDK version.
|
||||
*/
|
||||
INVALID_SPEC_VERSION,
|
||||
/**
|
||||
* The attribute name is not a valid/known context attribute.
|
||||
*/
|
||||
INVALID_ATTRIBUTE_NAME,
|
||||
/**
|
||||
* 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,
|
||||
/**
|
||||
* The attribute/extension type is not valid.
|
||||
*/
|
||||
INVALID_ATTRIBUTE_TYPE,
|
||||
/**
|
||||
* The attribute/extension value is not valid.
|
||||
*/
|
||||
INVALID_ATTRIBUTE_VALUE,
|
||||
/**
|
||||
* The data type is not valid.
|
||||
*/
|
||||
INVALID_DATA_TYPE,
|
||||
/**
|
||||
* Error while converting CloudEventData.
|
||||
*/
|
||||
DATA_CONVERSION,
|
||||
/**
|
||||
* Invalid content type or spec version
|
||||
*/
|
||||
UNKNOWN_ENCODING,
|
||||
/**
|
||||
* Other error.
|
||||
*/
|
||||
OTHER
|
||||
}
|
||||
|
||||
private final CloudEventRWExceptionKind kind;
|
||||
|
||||
private CloudEventRWException(CloudEventRWExceptionKind kind, Throwable cause) {
|
||||
super(cause);
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
private CloudEventRWException(CloudEventRWExceptionKind kind, String message) {
|
||||
super(message);
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
private CloudEventRWException(CloudEventRWExceptionKind kind, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
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,
|
||||
"Invalid specversion: " + specVersion
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
"Invalid attribute: " + attributeName
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
"Invalid extensions name: " + extensionName
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
"Invalid attribute/extension type for \"" + attributeName + "\": " + clazz.getCanonicalName()
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
cause
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
message = "Invalid data type: " + actual;
|
||||
} else {
|
||||
message = "Invalid data type: " + actual + ". Allowed: " + String.join(", ", allowed);
|
||||
}
|
||||
return new CloudEventRWException(
|
||||
CloudEventRWExceptionKind.INVALID_DATA_TYPE,
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
"Error while trying to convert data from " + from + " to " + to,
|
||||
cause
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
cause
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* An exception for use where none of the other variants are
|
||||
* appropriate.
|
||||
*
|
||||
* @param msg A description error message.
|
||||
* @return a new {@link CloudEventRWException}
|
||||
*/
|
||||
public static CloudEventRWException newOther(String msg){
|
||||
return new CloudEventRWException(CloudEventRWExceptionKind.OTHER, msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.CloudEventData;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Represents an object that can be read as CloudEvent.
|
||||
* <p>
|
||||
* The read may consume this object, hence it's not safe to invoke it multiple times, unless it's explicitly allowed by the implementer.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface CloudEventReader {
|
||||
|
||||
/**
|
||||
* 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 <W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory) throws CloudEventRWException {
|
||||
return read(writerFactory, CloudEventDataMapper.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
<W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException;
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.CloudEventData;
|
||||
|
||||
/**
|
||||
* Interface to write the content (CloudEvents attributes, extensions and payload) from a
|
||||
* {@link io.cloudevents.rw.CloudEventReader} to a new representation.
|
||||
*
|
||||
* @param <R> return value at the end of the write process
|
||||
*/
|
||||
public interface CloudEventWriter<R> extends CloudEventContextWriter {
|
||||
|
||||
/**
|
||||
* 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 write.
|
||||
*
|
||||
* @return an eventual return value
|
||||
* @throws CloudEventRWException if the message writer cannot be ended.
|
||||
*/
|
||||
R end() throws CloudEventRWException;
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.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<W extends CloudEventWriter<R>, R> {
|
||||
|
||||
/**
|
||||
* Create a {@link CloudEventWriter} starting from the provided {@link SpecVersion}
|
||||
*
|
||||
* @param version the spec version to create the writer
|
||||
* @return the new writer
|
||||
* @throws CloudEventRWException if the spec version is invalid or the writer cannot be instantiated.
|
||||
*/
|
||||
W create(SpecVersion version) throws CloudEventRWException;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.types;
|
||||
|
||||
import io.cloudevents.rw.CloudEventRWException;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
|
||||
|
||||
/**
|
||||
* Utilities to handle the <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#type-system">CloudEvent Attribute Timestamp type</a>
|
||||
*/
|
||||
public final class Time {
|
||||
|
||||
private Time() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a {@link String} RFC3339 compliant as {@link OffsetDateTime}.
|
||||
*
|
||||
* @param time the value to parse as time
|
||||
* @return the parsed {@link OffsetDateTime}
|
||||
* @throws DateTimeParseException if something went wrong when parsing the provided time.
|
||||
*/
|
||||
public static OffsetDateTime parseTime(String time) throws DateTimeParseException {
|
||||
return OffsetDateTime.parse(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an attribute/extension with RFC3339 compliant {@link String} value as {@link OffsetDateTime}.
|
||||
*
|
||||
* @param attributeName the attribute/extension name
|
||||
* @param time the value to parse as time
|
||||
* @return the parsed {@link OffsetDateTime}
|
||||
* @throws CloudEventRWException if something went wrong when parsing the attribute/extension.
|
||||
*/
|
||||
public static OffsetDateTime parseTime(String attributeName, String time) throws CloudEventRWException {
|
||||
try {
|
||||
return parseTime(time);
|
||||
} catch (DateTimeParseException e) {
|
||||
throw CloudEventRWException.newInvalidAttributeValue(attributeName, time, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a {@link OffsetDateTime} to a RFC3339 compliant {@link String}.
|
||||
*
|
||||
* @param time the time to write as {@link String}
|
||||
* @return the serialized time
|
||||
* @throws DateTimeException if something went wrong when serializing the provided time.
|
||||
*/
|
||||
public static String writeTime(OffsetDateTime time) throws DateTimeException {
|
||||
return ISO_OFFSET_DATE_TIME.format(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an attribute/extension {@link OffsetDateTime} to a RFC3339 compliant {@link String}.
|
||||
*
|
||||
* @param attributeName the attribute/extension name
|
||||
* @param time the time to write as {@link String}
|
||||
* @return the serialized time
|
||||
* @throws CloudEventRWException if something went wrong when serializing the attribute/extension.
|
||||
*/
|
||||
public static String writeTime(String attributeName, OffsetDateTime time) throws DateTimeException {
|
||||
try {
|
||||
return writeTime(time);
|
||||
} catch (DateTimeParseException e) {
|
||||
throw CloudEventRWException.newInvalidAttributeValue(attributeName, time, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v02;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.fun.ExtensionFormatAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public final class Accessor {
|
||||
private Accessor() {}
|
||||
|
||||
/**
|
||||
* To get access the set of {@link ExtensionFormat} inside the
|
||||
* event.
|
||||
*
|
||||
* <br>
|
||||
* <br>
|
||||
* This method follow the signature of
|
||||
* {@link ExtensionFormatAccessor#extensionsOf(CloudEvent)}
|
||||
*
|
||||
* @param cloudEvent
|
||||
* @throws IllegalArgumentException When argument is not an instance of {@link CloudEventImpl}
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static <A extends Attributes, T> Collection<ExtensionFormat>
|
||||
extensionsOf(CloudEvent<A, T> cloudEvent) {
|
||||
Objects.requireNonNull(cloudEvent);
|
||||
|
||||
if(cloudEvent instanceof CloudEventImpl) {
|
||||
CloudEventImpl impl = (CloudEventImpl)cloudEvent;
|
||||
return impl.getExtensionsFormats();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid instance type: "
|
||||
+ cloudEvent.getClass());
|
||||
}
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v02;
|
||||
|
||||
import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.json.ZonedDateTimeDeserializer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.2
|
||||
*/
|
||||
@JsonInclude(value = Include.NON_ABSENT)
|
||||
public class AttributesImpl implements Attributes {
|
||||
|
||||
@NotBlank
|
||||
private final String type;
|
||||
|
||||
@NotBlank
|
||||
@Pattern(regexp = "0\\.2")
|
||||
private final String specversion;
|
||||
|
||||
@NotNull
|
||||
private final URI source;
|
||||
|
||||
@NotBlank
|
||||
private final String id;
|
||||
|
||||
private final ZonedDateTime time;
|
||||
private final URI schemaurl;
|
||||
private final String contenttype;
|
||||
|
||||
AttributesImpl(String type, String specversion, URI source,
|
||||
String id, ZonedDateTime time, URI schemaurl, String contenttype) {
|
||||
this.type = type;
|
||||
this.specversion = specversion;
|
||||
this.source = source;
|
||||
this.id = id;
|
||||
this.time = time;
|
||||
this.schemaurl = schemaurl;
|
||||
this.contenttype = contenttype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of occurrence which has happened. Often this property is used for
|
||||
* routing, observability, policy enforcement, etc.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID of the event. The semantics of this string are explicitly
|
||||
* undefined to ease the implementation of producers. Enables
|
||||
* deduplication.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The version of the CloudEvents specification which the event uses.
|
||||
* This enables the interpretation of the context.
|
||||
*/
|
||||
public String getSpecversion() {
|
||||
return specversion;
|
||||
}
|
||||
|
||||
/**
|
||||
* This describes the event producer. Often this will include
|
||||
* information such as the type of the event source, the organization
|
||||
* publishing the event, and some unique identifiers.
|
||||
* The exact syntax and semantics behind the data encoded in the URI
|
||||
* is event producer defined.
|
||||
*/
|
||||
public URI getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamp of when the event happened.
|
||||
*/
|
||||
@JsonDeserialize(using = ZonedDateTimeDeserializer.class)
|
||||
public Optional<ZonedDateTime> getTime() {
|
||||
return Optional.ofNullable(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* A link to the schema that the data attribute adheres to.
|
||||
*/
|
||||
public Optional<URI> getSchemaurl() {
|
||||
return Optional.ofNullable(schemaurl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the data encoding format
|
||||
*/
|
||||
public Optional<String> getContenttype() {
|
||||
return Optional.ofNullable(contenttype);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Optional<String> getMediaType() {
|
||||
return getContenttype();
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static AttributesImpl build(
|
||||
@JsonProperty("id") String id,
|
||||
@JsonProperty("source") URI source,
|
||||
@JsonProperty("specversion") String specversion,
|
||||
@JsonProperty("type") String type,
|
||||
@JsonProperty("time") ZonedDateTime time,
|
||||
@JsonProperty("schemaurl") URI schemaurl,
|
||||
@JsonProperty("contenttype") String contenttype) {
|
||||
|
||||
return new AttributesImpl(type, specversion, source, id, time,
|
||||
schemaurl, contenttype);
|
||||
}
|
||||
|
||||
/**
|
||||
* The attribute unmarshaller for the binary format, that receives a
|
||||
* {@code Map} with attributes names as String and values as String.
|
||||
*/
|
||||
public static AttributesImpl unmarshal(Map<String, String> attributes) {
|
||||
String type = attributes.get(ContextAttributes.type.name());
|
||||
ZonedDateTime time =
|
||||
Optional.ofNullable(attributes.get(ContextAttributes.time.name()))
|
||||
.map((t) -> ZonedDateTime.parse(t,
|
||||
ISO_ZONED_DATE_TIME))
|
||||
.orElse(null);
|
||||
|
||||
String specversion = attributes.get(ContextAttributes.specversion.name());
|
||||
URI source = URI.create(attributes.get(ContextAttributes.source.name()));
|
||||
|
||||
URI schemaurl =
|
||||
Optional.ofNullable(attributes.get(ContextAttributes.schemaurl.name()))
|
||||
.map(URI::create)
|
||||
.orElse(null);
|
||||
|
||||
String id = attributes.get(ContextAttributes.id.name());
|
||||
|
||||
String contenttype =
|
||||
attributes.get(ContextAttributes.contenttype.name());
|
||||
|
||||
|
||||
return AttributesImpl.build(id, source, specversion, type,
|
||||
time, schemaurl, contenttype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the marshaller instance to marshall {@link AttributesImpl} as
|
||||
* a {@link Map} of strings
|
||||
*/
|
||||
public static Map<String, String> marshal(AttributesImpl attributes) {
|
||||
Objects.requireNonNull(attributes);
|
||||
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
||||
result.put(ContextAttributes.type.name(),
|
||||
attributes.getType());
|
||||
result.put(ContextAttributes.specversion.name(),
|
||||
attributes.getSpecversion());
|
||||
result.put(ContextAttributes.source.name(),
|
||||
attributes.getSource().toString());
|
||||
result.put(ContextAttributes.id.name(),
|
||||
attributes.getId());
|
||||
|
||||
attributes.getTime().ifPresent((value) -> result.put(ContextAttributes.time.name(),
|
||||
value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)));
|
||||
attributes.getSchemaurl().ifPresent((schema) -> result.put(ContextAttributes.schemaurl.name(),
|
||||
schema.toString()));
|
||||
attributes.getContenttype().ifPresent((ct) -> result.put(ContextAttributes.contenttype.name(), ct));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,254 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 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.v02;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
|
||||
import io.cloudevents.Builder;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.fun.EventBuilder;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* CloudEvent instances builder
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.2
|
||||
*/
|
||||
public class CloudEventBuilder<T> implements EventBuilder<T, AttributesImpl>,
|
||||
Builder<AttributesImpl, T> {
|
||||
private CloudEventBuilder() {
|
||||
}
|
||||
|
||||
private static Validator VALIDATOR;
|
||||
|
||||
private static final String SPEC_VERSION = "0.2";
|
||||
private static final String MESSAGE_SEPARATOR = ", ";
|
||||
private static final String MESSAGE = "'%s' %s";
|
||||
private static final String ERR_MESSAGE = "invalid payload: %s";
|
||||
|
||||
private String type;
|
||||
private String id;
|
||||
private URI source;
|
||||
|
||||
private ZonedDateTime time;
|
||||
private URI schemaurl;
|
||||
private String contenttype;
|
||||
private T data;
|
||||
|
||||
private final Set<ExtensionFormat> extensions = new HashSet<>();
|
||||
private Validator validator;
|
||||
|
||||
private static Validator getValidator() {
|
||||
if (null == VALIDATOR) {
|
||||
VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
}
|
||||
return VALIDATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a brand new builder instance
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
public static <T> CloudEventBuilder<T> builder() {
|
||||
return new CloudEventBuilder<>();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @param base A base event to copy {@link CloudEvent#getAttributes()},
|
||||
* {@link CloudEvent#getData()} and {@link CloudEvent#getExtensions()}
|
||||
* @return
|
||||
*/
|
||||
public static <T> CloudEventBuilder<T> builder(
|
||||
CloudEvent<AttributesImpl, T> base) {
|
||||
|
||||
Objects.requireNonNull(base);
|
||||
|
||||
CloudEventBuilder<T> result = new CloudEventBuilder<>();
|
||||
|
||||
AttributesImpl attributes = base.getAttributes();
|
||||
|
||||
result
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent(result::withTime);
|
||||
attributes.getSchemaurl().ifPresent(result::withSchemaurl);
|
||||
attributes.getContenttype().ifPresent(result::withContenttype);
|
||||
Accessor.extensionsOf(base).forEach(result::withExtension);
|
||||
base.getData().ifPresent(result::withData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T> the type of 'data'
|
||||
* @param data the value of data
|
||||
* @param attributes the context attributes
|
||||
* @return An new {@link CloudEventImpl} immutable instance
|
||||
* @throws IllegalStateException When there are specification constraints
|
||||
* violations
|
||||
*/
|
||||
public static <T> CloudEventImpl<T> of(T data, AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions, Validator validator) {
|
||||
CloudEventBuilder<T> builder = new CloudEventBuilder<T>()
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent(builder::withTime);
|
||||
attributes.getSchemaurl().ifPresent(builder::withSchemaurl);
|
||||
attributes.getContenttype().ifPresent(builder::withContenttype);
|
||||
extensions.forEach(builder::withExtension);
|
||||
|
||||
return builder
|
||||
.withData(data)
|
||||
.withValidator(validator)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent<AttributesImpl, T> build(T data, AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions) {
|
||||
return CloudEventBuilder.of(data, attributes, extensions, this.validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @return An new {@link CloudEventImpl} immutable instance
|
||||
* @throws IllegalStateException When there are specification constraints
|
||||
* violations
|
||||
*/
|
||||
@Override
|
||||
public CloudEventImpl<T> build() {
|
||||
AttributesImpl attributes = new AttributesImpl(type, SPEC_VERSION,
|
||||
source, id, time, schemaurl, contenttype);
|
||||
|
||||
CloudEventImpl<T> event = new CloudEventImpl<>(attributes, data, extensions);
|
||||
|
||||
if (validator == null) {
|
||||
validator = getValidator();
|
||||
}
|
||||
Set<ConstraintViolation<Object>> violations =
|
||||
validator.validate(event);
|
||||
|
||||
violations.addAll(validator.validate(event.getAttributes()));
|
||||
|
||||
final String errs =
|
||||
violations.stream()
|
||||
.map(v -> format(MESSAGE, v.getPropertyPath(), v.getMessage()))
|
||||
.collect(Collectors.joining(MESSAGE_SEPARATOR));
|
||||
|
||||
Optional.ofNullable(
|
||||
"".equals(errs) ? null : errs
|
||||
|
||||
).ifPresent((e) -> {
|
||||
throw new IllegalStateException(format(ERR_MESSAGE, e));
|
||||
});
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSource(URI source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withTime(ZonedDateTime time) {
|
||||
this.time = time;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSchemaurl(URI schemaurl) {
|
||||
this.schemaurl = schemaurl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withContenttype(String contenttype) {
|
||||
this.contenttype = contenttype;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withData(T data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withExtension(ExtensionFormat extension) {
|
||||
this.extensions.add(extension);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public <TT> CloudEvent<AttributesImpl, TT>
|
||||
build(CloudEvent<AttributesImpl, T> base, String id, TT newData) {
|
||||
return build(base, id, newData, null);
|
||||
}
|
||||
|
||||
public <TT> CloudEvent<AttributesImpl, TT>
|
||||
build(CloudEvent<AttributesImpl, T> base, String id, TT newData, Validator validator) {
|
||||
Objects.requireNonNull(base);
|
||||
|
||||
AttributesImpl attributes = base.getAttributes();
|
||||
|
||||
CloudEventBuilder<TT> builder = new CloudEventBuilder<TT>()
|
||||
.withId(id)
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent(builder::withTime);
|
||||
attributes.getSchemaurl().ifPresent(builder::withSchemaurl);
|
||||
attributes.getContenttype().ifPresent(builder::withContenttype);
|
||||
Collection<ExtensionFormat> extensions = Accessor.extensionsOf(base);
|
||||
extensions.forEach(builder::withExtension);
|
||||
|
||||
return builder
|
||||
.withData(newData)
|
||||
.withValidator(validator)
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v02;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.extensions.InMemoryFormat;
|
||||
|
||||
/**
|
||||
* The event implementation
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@JsonInclude(value = Include.NON_ABSENT)
|
||||
public class CloudEventImpl<T> implements CloudEvent<AttributesImpl, T> {
|
||||
|
||||
@JsonIgnore
|
||||
@NotNull
|
||||
private final AttributesImpl attributes;
|
||||
|
||||
private final T data;
|
||||
|
||||
@NotNull
|
||||
private final Map<String, Object> extensions;
|
||||
|
||||
private final Set<ExtensionFormat> extensionsFormats;
|
||||
|
||||
CloudEventImpl(AttributesImpl attributes, T data,
|
||||
Set<ExtensionFormat> extensions) {
|
||||
|
||||
this.attributes = attributes;
|
||||
|
||||
this.data = data;
|
||||
|
||||
this.extensions = extensions.stream()
|
||||
.map(ExtensionFormat::memory)
|
||||
.collect(Collectors.toMap(InMemoryFormat::getKey,
|
||||
InMemoryFormat::getValue));
|
||||
|
||||
this.extensionsFormats = extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the {@link Accessor} to access the set of {@link ExtensionFormat}
|
||||
*/
|
||||
Set<ExtensionFormat> getExtensionsFormats() {
|
||||
return extensionsFormats;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonUnwrapped
|
||||
public AttributesImpl getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event payload. The payload depends on the eventType,
|
||||
* schemaURL and eventTypeVersion, the payload is encoded into
|
||||
* a media format which is specified by the contentType attribute
|
||||
* (e.g. application/json).
|
||||
*/
|
||||
@Override
|
||||
public Optional<T> getData() {
|
||||
return Optional.ofNullable(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getDataBase64() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> getExtensions() {
|
||||
return Collections.unmodifiableMap(extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* The unique method that allows mutation. Used by
|
||||
* Jackson Framework to inject the extensions.
|
||||
*
|
||||
* @param name Extension name
|
||||
* @param value Extension value
|
||||
*/
|
||||
@JsonAnySetter
|
||||
void addExtension(String name, Object value) {
|
||||
extensions.put(name, value);
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static <T> CloudEventImpl<T> build(
|
||||
@JsonProperty("id") String id,
|
||||
@JsonProperty("source") URI source,
|
||||
@JsonProperty("type") String type,
|
||||
@JsonProperty("time") ZonedDateTime time,
|
||||
@JsonProperty("schemaurl") URI schemaurl,
|
||||
@JsonProperty("contenttype") String contenttype,
|
||||
@JsonProperty("data") T data) {
|
||||
|
||||
return CloudEventBuilder.<T>builder()
|
||||
.withId(id)
|
||||
.withSource(source)
|
||||
.withType(type)
|
||||
.withTime(time)
|
||||
.withSchemaurl(schemaurl)
|
||||
.withContenttype(contenttype)
|
||||
.withData(data)
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v02;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The specification reserved words: the context attributes
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public enum ContextAttributes {
|
||||
|
||||
id,
|
||||
source,
|
||||
specversion,
|
||||
type,
|
||||
time,
|
||||
schemaurl,
|
||||
contenttype;
|
||||
|
||||
public static final List<String> VALUES =
|
||||
Arrays.stream(ContextAttributes.values())
|
||||
.map(Enum::name)
|
||||
.collect(Collectors.toList());
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v02.http;
|
||||
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.cloudevents.fun.BinaryFormatAttributeMapper;
|
||||
import io.cloudevents.v02.ContextAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.2
|
||||
*/
|
||||
public class AttributeMapper {
|
||||
private AttributeMapper() {}
|
||||
|
||||
static final String HEADER_PREFIX = "ce-";
|
||||
|
||||
/**
|
||||
* Following the signature of {@link BinaryFormatAttributeMapper#map(Map)}
|
||||
* @param headers Map of HTTP request
|
||||
* @return Map with spec attributes and values without parsing
|
||||
* @see ContextAttributes
|
||||
*/
|
||||
public static Map<String, String> map(final Map<String, Object> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
final AtomicReference<Optional<Entry<String, Object>>> ct =
|
||||
new AtomicReference<>();
|
||||
|
||||
ct.set(Optional.empty());
|
||||
|
||||
Map<String, String> result = headers.entrySet()
|
||||
.stream()
|
||||
.filter(header -> null!= header.getValue())
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.toLowerCase(Locale.US), header.getValue()))
|
||||
.peek(header -> {
|
||||
if("content-type".equals(header.getKey())) {
|
||||
ct.set(Optional.ofNullable(header));
|
||||
}
|
||||
})
|
||||
.filter(header -> header.getKey().startsWith(HEADER_PREFIX))
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.substring(HEADER_PREFIX.length()), header.getValue()))
|
||||
|
||||
.map(header -> new SimpleEntry<>(header.getKey(),
|
||||
header.getValue().toString()))
|
||||
|
||||
.collect(toMap(Entry::getKey, Entry::getValue));
|
||||
|
||||
ct.get().ifPresent(contentType -> {
|
||||
result.put(ContextAttributes.contenttype.name(),
|
||||
contentType.getValue().toString());
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v02.http;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.cloudevents.fun.FormatExtensionMapper;
|
||||
import io.cloudevents.v02.ContextAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.2
|
||||
*/
|
||||
public class ExtensionMapper {
|
||||
private ExtensionMapper() {}
|
||||
|
||||
private static final List<String> RESERVED_HEADERS =
|
||||
ContextAttributes.VALUES.stream()
|
||||
.map(attribute -> AttributeMapper
|
||||
.HEADER_PREFIX + attribute)
|
||||
.collect(Collectors.toList());
|
||||
static {
|
||||
RESERVED_HEADERS.add("content-type");
|
||||
};
|
||||
|
||||
/**
|
||||
* Following the signature of {@link FormatExtensionMapper}
|
||||
* @param headers The HTTP headers
|
||||
* @return The potential extensions without parsing
|
||||
*/
|
||||
public static Map<String, String> map(Map<String, Object> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
// remove all reserved words and the remaining may be extensions
|
||||
return
|
||||
headers.entrySet()
|
||||
.stream()
|
||||
.filter(header -> null!= header.getValue())
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.toLowerCase(Locale.US), header.getValue().toString()))
|
||||
.filter(header -> !RESERVED_HEADERS.contains(header.getKey()))
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v02.http;
|
||||
|
||||
import static io.cloudevents.v02.http.AttributeMapper.HEADER_PREFIX;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.cloudevents.fun.FormatHeaderMapper;
|
||||
import io.cloudevents.v02.ContextAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.2
|
||||
*/
|
||||
public final class HeaderMapper {
|
||||
private HeaderMapper() {}
|
||||
|
||||
private static final String HTTP_CONTENT_TYPE = "Content-Type";
|
||||
|
||||
/**
|
||||
* Following the signature of {@link FormatHeaderMapper}
|
||||
* @param attributes The map of attributes created by {@link AttributeMapper}
|
||||
* @param extensions The map of extensions created by {@link ExtensionMapper}
|
||||
* @return The map of HTTP Headers
|
||||
*/
|
||||
public static Map<String, String> map(Map<String, String> attributes,
|
||||
Map<String, String> extensions) {
|
||||
Objects.requireNonNull(attributes);
|
||||
Objects.requireNonNull(extensions);
|
||||
|
||||
Map<String, String> result = attributes.entrySet()
|
||||
.stream()
|
||||
.filter(attribute -> null!= attribute.getValue())
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.toLowerCase(Locale.US), header.getValue()))
|
||||
.filter(header -> !header.getKey()
|
||||
.equals(ContextAttributes.contenttype.name()))
|
||||
.map(header -> new SimpleEntry<>(HEADER_PREFIX+header.getKey(),
|
||||
header.getValue()))
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
|
||||
result.putAll(
|
||||
extensions.entrySet()
|
||||
.stream()
|
||||
.filter(extension -> null!= extension.getValue())
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue))
|
||||
);
|
||||
|
||||
Optional.ofNullable(attributes.get(ContextAttributes.contenttype.name()))
|
||||
.ifPresent((ct) -> {
|
||||
result.put(HTTP_CONTENT_TYPE, ct);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v02.http;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.format.BinaryMarshaller;
|
||||
import io.cloudevents.format.StructuredMarshaller;
|
||||
import io.cloudevents.format.Wire;
|
||||
import io.cloudevents.format.builder.EventStep;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.v02.Accessor;
|
||||
import io.cloudevents.v02.AttributesImpl;
|
||||
import io.cloudevents.v02.CloudEventImpl;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.2
|
||||
*/
|
||||
public class Marshallers {
|
||||
private Marshallers() {}
|
||||
|
||||
private static final Map<String, String> NO_HEADERS =
|
||||
new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Builds a Binary Content Mode marshaller to marshal cloud events as JSON for
|
||||
* HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @return A step to provide the {@link CloudEventImpl} and marshal as JSON
|
||||
* @see BinaryMarshaller
|
||||
*/
|
||||
public static <T> EventStep<AttributesImpl, T, String, String> binary() {
|
||||
return
|
||||
BinaryMarshaller.<AttributesImpl, T, String, String>
|
||||
builder()
|
||||
.map(AttributesImpl::marshal)
|
||||
.map(Accessor::extensionsOf)
|
||||
.map(ExtensionFormat::marshal)
|
||||
.map(HeaderMapper::map)
|
||||
.map(Json.<T, String>marshaller()::marshal)
|
||||
.builder(Wire<String, String, String>::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode marshaller to marshal cloud event as JSON for
|
||||
* HTTP Transport Binding
|
||||
* @param <T> The 'data' type
|
||||
* @return A step to provider the {@link CloudEventImpl} and marshal as JSON
|
||||
* @see StructuredMarshaller
|
||||
*/
|
||||
public static <T> EventStep<AttributesImpl, T, String, String> structured() {
|
||||
return
|
||||
StructuredMarshaller.
|
||||
<AttributesImpl, T, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((event) -> {
|
||||
return Json.<CloudEvent<AttributesImpl, T>, String>
|
||||
marshaller().marshal(event, NO_HEADERS);
|
||||
})
|
||||
.map(Accessor::extensionsOf)
|
||||
.map(ExtensionFormat::marshal)
|
||||
.map(HeaderMapper::map);
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
package io.cloudevents.v02.http;
|
||||
|
||||
import javax.validation.Validator;
|
||||
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
import io.cloudevents.format.BinaryUnmarshaller;
|
||||
import io.cloudevents.format.StructuredUnmarshaller;
|
||||
import io.cloudevents.format.builder.HeadersStep;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.v02.AttributesImpl;
|
||||
import io.cloudevents.v02.CloudEventBuilder;
|
||||
import io.cloudevents.v02.CloudEventImpl;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.2
|
||||
*/
|
||||
public class Unmarshallers {
|
||||
private Unmarshallers() {}
|
||||
|
||||
/**
|
||||
* Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @param type The type reference to use for 'data' unmarshal
|
||||
* @return A step to supply the headers, payload and to unmarshal
|
||||
* @see BinaryUnmarshaller
|
||||
*/
|
||||
public static <T> HeadersStep<AttributesImpl, T, String>
|
||||
binary(Class<T> type) {
|
||||
return binary(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @param type The type reference to use for 'data' unmarshal
|
||||
* @param validator Provide an existing instance of a {@link Validator}
|
||||
* @return A step to supply the headers, payload and to unmarshal
|
||||
* @see BinaryUnmarshaller
|
||||
*/
|
||||
public static <T> HeadersStep<AttributesImpl, T, String>
|
||||
binary(Class<T> type, Validator validator) {
|
||||
return
|
||||
BinaryUnmarshaller.<AttributesImpl, T, String>builder()
|
||||
.map(AttributeMapper::map)
|
||||
.map(AttributesImpl::unmarshal)
|
||||
.map("application/json", Json.umarshaller(type)::unmarshal)
|
||||
.next()
|
||||
.map(ExtensionMapper::map)
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
.next()
|
||||
.builder(CloudEventBuilder.<T>builder().withValidator(validator)::build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @param typeOfData The type reference to use for 'data' unmarshal
|
||||
* @return A step to supply the headers, payload and to unmarshal
|
||||
* @see StructuredUnmarshaller
|
||||
*/
|
||||
public static <T> HeadersStep<AttributesImpl, T, String>
|
||||
structured(Class<T> typeOfData) {
|
||||
return structured(typeOfData, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @param typeOfData The type reference to use for 'data' unmarshal
|
||||
* @param validator Provided instance of a {@link Validator}
|
||||
* @return A step to supply the headers, payload and to unmarshal
|
||||
* @see StructuredUnmarshaller
|
||||
*/
|
||||
public static <T> HeadersStep<AttributesImpl, T, String>
|
||||
structured(Class<T> typeOfData, Validator validator) {
|
||||
|
||||
return
|
||||
StructuredUnmarshaller.<AttributesImpl, T, String>
|
||||
builder()
|
||||
.map(ExtensionMapper::map)
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
.next()
|
||||
.map((payload, extensions) -> {
|
||||
CloudEventImpl<T> event =
|
||||
Json.decodeValue(payload, CloudEventImpl.class, typeOfData);
|
||||
CloudEventBuilder<T> builder =
|
||||
CloudEventBuilder.builder(event);
|
||||
extensions.get().forEach(builder::withExtension);
|
||||
|
||||
return builder.withValidator(validator).build();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package io.cloudevents.v03;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.fun.ExtensionFormatAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public class Accessor {
|
||||
private Accessor() {}
|
||||
|
||||
/**
|
||||
* To get access the set of {@link ExtensionFormat} inside the
|
||||
* event.
|
||||
*
|
||||
* <br>
|
||||
* <br>
|
||||
* This method follow the signature of
|
||||
* {@link ExtensionFormatAccessor#extensionsOf(CloudEvent)}
|
||||
*
|
||||
* @param cloudEvent
|
||||
* @throws IllegalArgumentException When argument is not an instance
|
||||
* of {@link CloudEventImpl}
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static <A extends Attributes, T> Collection<ExtensionFormat>
|
||||
extensionsOf(CloudEvent<A, T> cloudEvent) {
|
||||
Objects.requireNonNull(cloudEvent);
|
||||
|
||||
if(cloudEvent instanceof CloudEventImpl) {
|
||||
CloudEventImpl impl = (CloudEventImpl)cloudEvent;
|
||||
return impl.getExtensionsFormats();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid instance type: "
|
||||
+ cloudEvent.getClass());
|
||||
}
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v03;
|
||||
|
||||
import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.json.ZonedDateTimeDeserializer;
|
||||
|
||||
/**
|
||||
* The event attributes implementation for v0.3
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@JsonInclude(value = Include.NON_ABSENT)
|
||||
public class AttributesImpl implements Attributes {
|
||||
|
||||
@NotBlank
|
||||
private final String id;
|
||||
|
||||
@NotNull
|
||||
private final URI source;
|
||||
|
||||
@NotBlank
|
||||
@Pattern(regexp = "0\\.3")
|
||||
private final String specversion;
|
||||
|
||||
@NotBlank
|
||||
private final String type;
|
||||
|
||||
@JsonDeserialize(using = ZonedDateTimeDeserializer.class)
|
||||
private final ZonedDateTime time;
|
||||
private final URI schemaurl;
|
||||
|
||||
@Pattern(regexp = "base64")
|
||||
private final String datacontentencoding;
|
||||
private final String datacontenttype;
|
||||
|
||||
@Size(min = 1)
|
||||
private final String subject;
|
||||
|
||||
AttributesImpl(String id, URI source, String specversion, String type,
|
||||
ZonedDateTime time, URI schemaurl, String datacontentencoding,
|
||||
String datacontenttype, String subject) {
|
||||
this.id = id;
|
||||
this.source = source;
|
||||
this.specversion = specversion;
|
||||
this.type = type;
|
||||
|
||||
this.time = time;
|
||||
this.schemaurl = schemaurl;
|
||||
this.datacontentencoding = datacontentencoding;
|
||||
this.datacontenttype = datacontenttype;
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public URI getSource() {
|
||||
return source;
|
||||
}
|
||||
public String getSpecversion() {
|
||||
return specversion;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public Optional<ZonedDateTime> getTime() {
|
||||
return Optional.ofNullable(time);
|
||||
}
|
||||
public Optional<URI> getSchemaurl() {
|
||||
return Optional.ofNullable(schemaurl);
|
||||
}
|
||||
public Optional<String> getDatacontentencoding() {
|
||||
return Optional.ofNullable(datacontentencoding);
|
||||
}
|
||||
public Optional<String> getDatacontenttype() {
|
||||
return Optional.ofNullable(datacontenttype);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Optional<String> getMediaType() {
|
||||
return getDatacontenttype();
|
||||
}
|
||||
public Optional<String> getSubject() {
|
||||
return Optional.ofNullable(subject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AttributesImpl [id=" + id + ", source=" + source
|
||||
+ ", specversion=" + specversion + ", type=" + type
|
||||
+ ", time=" + time + ", schemaurl=" + schemaurl
|
||||
+ ", datacontentencoding=" + datacontentencoding
|
||||
+ ", datacontenttype=" + datacontenttype + ", subject="
|
||||
+ subject + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the Jackson framework to unmarshall.
|
||||
*/
|
||||
@JsonCreator
|
||||
public static AttributesImpl build(
|
||||
@JsonProperty("id") String id,
|
||||
@JsonProperty("source") URI source,
|
||||
@JsonProperty("specversion") String specversion,
|
||||
@JsonProperty("type") String type,
|
||||
@JsonProperty("time") ZonedDateTime time,
|
||||
@JsonProperty("schemaurl") URI schemaurl,
|
||||
@JsonProperty("datacontentenconding") String datacontentencoding,
|
||||
@JsonProperty("datacontenttype") String datacontenttype,
|
||||
@JsonProperty("subject") String subject) {
|
||||
|
||||
return new AttributesImpl(id, source, specversion, type, time,
|
||||
schemaurl, datacontentencoding, datacontenttype, subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the marshaller instance to marshall {@link AttributesImpl} as
|
||||
* a {@link Map} of strings
|
||||
*/
|
||||
public static Map<String, String> marshal(AttributesImpl attributes) {
|
||||
Objects.requireNonNull(attributes);
|
||||
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
||||
result.put(ContextAttributes.type.name(),
|
||||
attributes.getType());
|
||||
result.put(ContextAttributes.specversion.name(),
|
||||
attributes.getSpecversion());
|
||||
result.put(ContextAttributes.source.name(),
|
||||
attributes.getSource().toString());
|
||||
result.put(ContextAttributes.id.name(),
|
||||
attributes.getId());
|
||||
|
||||
attributes.getTime().ifPresent((value) -> result.put(ContextAttributes.time.name(),
|
||||
value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)));
|
||||
attributes.getSchemaurl().ifPresent((schema) -> result.put(ContextAttributes.schemaurl.name(),
|
||||
schema.toString()));
|
||||
attributes.getDatacontenttype().ifPresent((ct) -> result.put(ContextAttributes.datacontenttype.name(), ct));
|
||||
attributes.getDatacontentencoding().ifPresent(dce -> result.put(ContextAttributes.datacontentencoding.name(), dce));
|
||||
attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.subject.name(), subject));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The attribute unmarshaller for the binary format, that receives a
|
||||
* {@code Map} with attributes names as String and value as String.
|
||||
*/
|
||||
public static AttributesImpl unmarshal(Map<String, String> attributes) {
|
||||
String type = attributes.get(ContextAttributes.type.name());
|
||||
ZonedDateTime time =
|
||||
Optional.ofNullable(attributes.get(ContextAttributes.time.name()))
|
||||
.map((t) -> ZonedDateTime.parse(t,
|
||||
ISO_ZONED_DATE_TIME))
|
||||
.orElse(null);
|
||||
|
||||
String specversion = attributes.get(ContextAttributes.specversion.name());
|
||||
URI source = URI.create(attributes.get(ContextAttributes.source.name()));
|
||||
|
||||
URI schemaurl =
|
||||
Optional.ofNullable(attributes.get(ContextAttributes.schemaurl.name()))
|
||||
.map(URI::create)
|
||||
.orElse(null);
|
||||
|
||||
String id = attributes.get(ContextAttributes.id.name());
|
||||
|
||||
String datacontenttype =
|
||||
attributes.get(ContextAttributes.datacontenttype.name());
|
||||
|
||||
String datacontentencoding =
|
||||
attributes.get(ContextAttributes.datacontentencoding.name());
|
||||
|
||||
String subject = attributes.get(ContextAttributes.subject.name());
|
||||
|
||||
return AttributesImpl.build(id, source, specversion, type,
|
||||
time, schemaurl, datacontentencoding,
|
||||
datacontenttype, subject);
|
||||
}
|
||||
}
|
|
@ -1,257 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v03;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.fun.EventBuilder;
|
||||
|
||||
/**
|
||||
* The event builder.
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public final class CloudEventBuilder<T> implements
|
||||
EventBuilder<T, AttributesImpl> {
|
||||
|
||||
private CloudEventBuilder() {}
|
||||
|
||||
private static Validator VALIDATOR;
|
||||
|
||||
public static final String SPEC_VERSION = "0.3";
|
||||
private static final String MESSAGE_SEPARATOR = ", ";
|
||||
private static final String MESSAGE = "'%s' %s";
|
||||
private static final String ERR_MESSAGE = "invalid payload: %s";
|
||||
|
||||
private String id;
|
||||
private URI source;
|
||||
|
||||
private String type;
|
||||
|
||||
private ZonedDateTime time;
|
||||
private URI schemaurl;
|
||||
private String datacontentencoding;
|
||||
private String datacontenttype;
|
||||
private String subject;
|
||||
|
||||
private T data;
|
||||
|
||||
private final Set<ExtensionFormat> extensions = new HashSet<>();
|
||||
private Validator validator;
|
||||
|
||||
private static Validator getValidator() {
|
||||
if(null== VALIDATOR) {
|
||||
VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
}
|
||||
return VALIDATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a brand new builder instance
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
public static <T> CloudEventBuilder<T> builder() {
|
||||
return new CloudEventBuilder<>();
|
||||
}
|
||||
|
||||
public static <T> CloudEventBuilder<T> builder(
|
||||
CloudEvent<AttributesImpl, T> base) {
|
||||
Objects.requireNonNull(base);
|
||||
|
||||
CloudEventBuilder<T> result = new CloudEventBuilder<>();
|
||||
|
||||
AttributesImpl attributes = base.getAttributes();
|
||||
|
||||
result
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent(result::withTime);
|
||||
attributes.getSchemaurl().ifPresent(result::withSchemaurl);
|
||||
attributes.getDatacontenttype().ifPresent(result::withDatacontenttype);
|
||||
attributes.getDatacontentencoding().ifPresent(result::withDatacontentencoding);
|
||||
attributes.getSubject().ifPresent(result::withSubject);
|
||||
Accessor.extensionsOf(base).forEach(result::withExtension);
|
||||
base.getData().ifPresent(result::withData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an event from data and attributes
|
||||
* @param <T> the type of 'data'
|
||||
* @param data the value of data
|
||||
* @param attributes the context attributes
|
||||
* @param extensions the extension attributes
|
||||
* @return An new {@link CloudEventImpl} immutable instance
|
||||
* @throws IllegalStateException When there are specification constraints
|
||||
* violations
|
||||
*/
|
||||
public static <T> CloudEventImpl<T> of(T data, AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions) {
|
||||
return of(data, attributes, extensions, null);
|
||||
}
|
||||
/**
|
||||
* Build an event from data and attributes
|
||||
* @param <T> the type of 'data'
|
||||
* @param data the value of data
|
||||
* @param attributes the context attributes
|
||||
* @param extensions the extension attributes
|
||||
* @param validator existing instance of a validator
|
||||
* @return An new {@link CloudEventImpl} immutable instance
|
||||
* @throws IllegalStateException When there are specification constraints
|
||||
* violations
|
||||
*/
|
||||
public static <T> CloudEventImpl<T> of(T data, AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions, Validator validator) {
|
||||
CloudEventBuilder<T> builder = CloudEventBuilder.<T>builder()
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent(builder::withTime);
|
||||
attributes.getSchemaurl().ifPresent(builder::withSchemaurl);
|
||||
attributes.getDatacontentencoding().ifPresent(builder::withDatacontentencoding);
|
||||
attributes.getDatacontenttype().ifPresent(builder::withDatacontenttype);
|
||||
attributes.getSubject().ifPresent(builder::withSubject);
|
||||
extensions.forEach(builder::withExtension);
|
||||
|
||||
return builder
|
||||
.withData(data)
|
||||
.withValidator(validator)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent<AttributesImpl, T> build(T data, AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions){
|
||||
return CloudEventBuilder.of(data, attributes, extensions, this.validator);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return An new {@link CloudEvent} immutable instance
|
||||
* @throws IllegalStateException When there are specification constraints
|
||||
* violations
|
||||
*/
|
||||
public CloudEventImpl<T> build() {
|
||||
|
||||
AttributesImpl attributes = new AttributesImpl(id, source, SPEC_VERSION,
|
||||
type, time, schemaurl, datacontentencoding, datacontenttype,
|
||||
subject);
|
||||
|
||||
CloudEventImpl<T> cloudEvent =
|
||||
new CloudEventImpl<>(attributes, data, extensions);
|
||||
|
||||
if(validator == null) {
|
||||
validator = getValidator();
|
||||
}
|
||||
|
||||
Set<ConstraintViolation<Object>> violations =
|
||||
validator.validate(cloudEvent);
|
||||
|
||||
violations.addAll(validator.validate(cloudEvent.getAttributes()));
|
||||
|
||||
final String errs =
|
||||
violations.stream()
|
||||
.map(v -> format(MESSAGE, v.getPropertyPath(), v.getMessage()))
|
||||
.collect(Collectors.joining(MESSAGE_SEPARATOR));
|
||||
|
||||
Optional.ofNullable(
|
||||
"".equals(errs) ? null : errs
|
||||
|
||||
).ifPresent((e) -> {
|
||||
throw new IllegalStateException(format(ERR_MESSAGE, e));
|
||||
});
|
||||
|
||||
return cloudEvent;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSource(URI source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withTime(ZonedDateTime time) {
|
||||
this.time = time;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSchemaurl(URI schemaurl) {
|
||||
this.schemaurl = schemaurl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withDatacontentencoding(
|
||||
String datacontentencoding) {
|
||||
this.datacontentencoding = datacontentencoding;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withDatacontenttype(
|
||||
String datacontenttype) {
|
||||
this.datacontenttype = datacontenttype;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSubject(
|
||||
String subject) {
|
||||
this.subject = subject;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withData(T data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withExtension(ExtensionFormat extension) {
|
||||
this.extensions.add(extension);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v03;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.extensions.InMemoryFormat;
|
||||
|
||||
/**
|
||||
* The event implementation
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@JsonInclude(value = Include.NON_ABSENT)
|
||||
public class CloudEventImpl<T> implements CloudEvent<AttributesImpl, T> {
|
||||
|
||||
@JsonIgnore
|
||||
@NotNull
|
||||
private final AttributesImpl attributes;
|
||||
|
||||
private final T data;
|
||||
|
||||
@NotNull
|
||||
private final Map<String, Object> extensions;
|
||||
|
||||
private final Set<ExtensionFormat> extensionsFormats;
|
||||
|
||||
CloudEventImpl(AttributesImpl attributes, T data,
|
||||
Set<ExtensionFormat> extensions) {
|
||||
this.attributes = attributes;
|
||||
this.data = data;
|
||||
|
||||
this.extensions = extensions.stream()
|
||||
.map(ExtensionFormat::memory)
|
||||
.collect(Collectors.toMap(InMemoryFormat::getKey,
|
||||
InMemoryFormat::getValue));
|
||||
|
||||
this.extensionsFormats = extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the {@link Accessor} to access the set of {@link ExtensionFormat}
|
||||
*/
|
||||
Set<ExtensionFormat> getExtensionsFormats() {
|
||||
return extensionsFormats;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonUnwrapped
|
||||
public AttributesImpl getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> getData() {
|
||||
return Optional.ofNullable(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getDataBase64() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> getExtensions() {
|
||||
return Collections.unmodifiableMap(extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* The unique method that allows mutation. Used by
|
||||
* Jackson Framework to inject the extensions.
|
||||
*
|
||||
* @param name Extension name
|
||||
* @param value Extension value
|
||||
*/
|
||||
@JsonAnySetter
|
||||
void addExtension(String name, Object value) {
|
||||
extensions.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the Jackson Framework to unmarshall.
|
||||
*/
|
||||
@JsonCreator
|
||||
public static <T> CloudEventImpl<T> build(
|
||||
@JsonProperty("id") String id,
|
||||
@JsonProperty("source") URI source,
|
||||
@JsonProperty("type") String type,
|
||||
@JsonProperty("time") ZonedDateTime time,
|
||||
@JsonProperty("schemaurl") URI schemaurl,
|
||||
@JsonProperty("datacontentencoding") String datacontentencoding,
|
||||
@JsonProperty("datacontenttype") String datacontenttype,
|
||||
@JsonProperty("subject") String subject,
|
||||
@JsonProperty("data") T data) {
|
||||
|
||||
return CloudEventBuilder.<T>builder()
|
||||
.withId(id)
|
||||
.withSource(source)
|
||||
.withType(type)
|
||||
.withTime(time)
|
||||
.withSchemaurl(schemaurl)
|
||||
.withDatacontentencoding(datacontentencoding)
|
||||
.withDatacontenttype(datacontenttype)
|
||||
.withData(data)
|
||||
.withSubject(subject)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v03;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The specification reserved words: the context attributes
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public enum ContextAttributes {
|
||||
|
||||
id,
|
||||
source,
|
||||
specversion,
|
||||
type,
|
||||
time,
|
||||
schemaurl,
|
||||
datacontenttype,
|
||||
datacontentencoding,
|
||||
subject;
|
||||
|
||||
public static final List<String> VALUES =
|
||||
Arrays.stream(ContextAttributes.values())
|
||||
.map(Enum::name)
|
||||
.collect(Collectors.toList());
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v03.http;
|
||||
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.cloudevents.fun.BinaryFormatAttributeMapper;
|
||||
import io.cloudevents.v03.ContextAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.3
|
||||
*/
|
||||
public class AttributeMapper {
|
||||
private AttributeMapper() {}
|
||||
|
||||
static final String HEADER_PREFIX = "ce-";
|
||||
|
||||
/**
|
||||
* Following the signature of {@link BinaryFormatAttributeMapper#map(Map)}
|
||||
* @param headers Map of HTTP request
|
||||
* @return Map with spec attributes and values without parsing
|
||||
* @see ContextAttributes
|
||||
*/
|
||||
public static Map<String, String> map(final Map<String, Object> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
final AtomicReference<Optional<Entry<String, Object>>> ct =
|
||||
new AtomicReference<>();
|
||||
|
||||
ct.set(Optional.empty());
|
||||
|
||||
Map<String, String> result = headers.entrySet()
|
||||
.stream()
|
||||
.filter(header -> null!= header.getValue())
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.toLowerCase(Locale.US), header.getValue()))
|
||||
.peek(header -> {
|
||||
if("content-type".equals(header.getKey())) {
|
||||
ct.set(Optional.ofNullable(header));
|
||||
}
|
||||
})
|
||||
.filter(header -> header.getKey().startsWith(HEADER_PREFIX))
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.substring(HEADER_PREFIX.length()), header.getValue()))
|
||||
.map(header -> new SimpleEntry<>(header.getKey(),
|
||||
header.getValue().toString()))
|
||||
.collect(toMap(Entry::getKey, Entry::getValue));
|
||||
|
||||
ct.get().ifPresent(contentType -> {
|
||||
result.put(ContextAttributes.datacontenttype.name(),
|
||||
contentType.getValue().toString());
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v03.http;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.cloudevents.fun.FormatExtensionMapper;
|
||||
import io.cloudevents.v03.ContextAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.3
|
||||
*/
|
||||
public class ExtensionMapper {
|
||||
private ExtensionMapper() {}
|
||||
|
||||
private static final List<String> RESERVED_HEADERS =
|
||||
ContextAttributes.VALUES.stream()
|
||||
.map(attribute -> AttributeMapper
|
||||
.HEADER_PREFIX + attribute)
|
||||
.collect(Collectors.toList());
|
||||
static {
|
||||
RESERVED_HEADERS.add("content-type");
|
||||
};
|
||||
|
||||
/**
|
||||
* Following the signature of {@link FormatExtensionMapper}
|
||||
* @param headers The HTTP headers
|
||||
* @return The potential extensions without parsing
|
||||
*/
|
||||
public static Map<String, String> map(Map<String, Object> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
// remove all reserved words and the remaining may be extensions
|
||||
return
|
||||
headers.entrySet()
|
||||
.stream()
|
||||
.filter(header -> null!= header.getValue())
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.toLowerCase(Locale.US), header.getValue().toString()))
|
||||
.filter(header -> !RESERVED_HEADERS.contains(header.getKey()))
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v03.http;
|
||||
|
||||
import static io.cloudevents.v03.http.AttributeMapper.HEADER_PREFIX;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.cloudevents.fun.FormatHeaderMapper;
|
||||
import io.cloudevents.v03.ContextAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public class HeaderMapper {
|
||||
private HeaderMapper() {}
|
||||
|
||||
private static final String HTTP_CONTENT_TYPE = "Content-Type";
|
||||
|
||||
/**
|
||||
* Following the signature of {@link FormatHeaderMapper}
|
||||
* @param attributes The map of attributes created by {@link AttributeMapper}
|
||||
* @param extensions The map of extensions created by {@link ExtensionMapper}
|
||||
* @return The map of HTTP Headers
|
||||
*/
|
||||
public static Map<String, String> map(Map<String, String> attributes,
|
||||
Map<String, String> extensions) {
|
||||
Objects.requireNonNull(attributes);
|
||||
Objects.requireNonNull(extensions);
|
||||
|
||||
Map<String, String> result = attributes.entrySet()
|
||||
.stream()
|
||||
.filter(attribute -> null!= attribute.getValue())
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.toLowerCase(Locale.US), header.getValue()))
|
||||
.filter(header -> !header.getKey()
|
||||
.equals(ContextAttributes.datacontenttype.name()))
|
||||
.map(header -> new SimpleEntry<>(HEADER_PREFIX+header.getKey(),
|
||||
header.getValue()))
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
|
||||
result.putAll(
|
||||
extensions.entrySet()
|
||||
.stream()
|
||||
.filter(extension -> null!= extension.getValue())
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue))
|
||||
);
|
||||
|
||||
Optional.ofNullable(attributes
|
||||
.get(ContextAttributes.datacontenttype.name()))
|
||||
.ifPresent((dct) -> {
|
||||
result.put(HTTP_CONTENT_TYPE, dct);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v03.http;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.format.BinaryMarshaller;
|
||||
import io.cloudevents.format.StructuredMarshaller;
|
||||
import io.cloudevents.format.Wire;
|
||||
import io.cloudevents.format.builder.EventStep;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.v03.Accessor;
|
||||
import io.cloudevents.v03.AttributesImpl;
|
||||
import io.cloudevents.v03.CloudEventImpl;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.3
|
||||
*/
|
||||
public class Marshallers {
|
||||
private Marshallers() {}
|
||||
|
||||
private static final Map<String, String> NO_HEADERS =
|
||||
new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Builds a Binary Content Mode marshaller to marshal cloud events as JSON for
|
||||
* HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @return A step to provide the {@link CloudEventImpl} and marshal as JSON
|
||||
* @see BinaryMarshaller
|
||||
*/
|
||||
public static <T> EventStep<AttributesImpl, T, String, String> binary() {
|
||||
return
|
||||
BinaryMarshaller.<AttributesImpl, T, String, String>
|
||||
builder()
|
||||
.map(AttributesImpl::marshal)
|
||||
.map(Accessor::extensionsOf)
|
||||
.map(ExtensionFormat::marshal)
|
||||
.map(HeaderMapper::map)
|
||||
.map(Json.<T, String>marshaller()::marshal)
|
||||
.builder(Wire<String, String, String>::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode marshaller to marshal cloud event as JSON for
|
||||
* HTTP Transport Binding
|
||||
* @param <T> The 'data' type
|
||||
* @return A step to provider the {@link CloudEventImpl} and marshal as JSON
|
||||
* @see StructuredMarshaller
|
||||
*/
|
||||
public static <T> EventStep<AttributesImpl, T, String, String> structured() {
|
||||
return
|
||||
StructuredMarshaller.
|
||||
<AttributesImpl, T, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((event) -> {
|
||||
return Json.<CloudEvent<AttributesImpl, T>, String>
|
||||
marshaller().marshal(event, NO_HEADERS);
|
||||
})
|
||||
.map(Accessor::extensionsOf)
|
||||
.map(ExtensionFormat::marshal)
|
||||
.map(HeaderMapper::map);
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v03.http;
|
||||
|
||||
import javax.validation.Validator;
|
||||
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
import io.cloudevents.format.BinaryUnmarshaller;
|
||||
import io.cloudevents.format.StructuredUnmarshaller;
|
||||
import io.cloudevents.format.builder.HeadersStep;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.v03.AttributesImpl;
|
||||
import io.cloudevents.v03.CloudEventBuilder;
|
||||
import io.cloudevents.v03.CloudEventImpl;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 0.3
|
||||
*/
|
||||
public class Unmarshallers {
|
||||
private Unmarshallers() {}
|
||||
|
||||
/**
|
||||
* Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @param type The type reference to use for 'data' unmarshal
|
||||
* @return A step to supply the headers, payload and to unmarshal
|
||||
* @see BinaryUnmarshaller
|
||||
*/
|
||||
public static <T> HeadersStep<AttributesImpl, T, String>
|
||||
binary(Class<T> type) {
|
||||
return binary(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @param type The type reference to use for 'data' unmarshal
|
||||
* @param validator Provided instance of a {@link Validator}
|
||||
* @return A step to supply the headers, payload and to unmarshal
|
||||
* @see BinaryUnmarshaller
|
||||
*/
|
||||
public static <T> HeadersStep<AttributesImpl, T, String>
|
||||
binary(Class<T> type, Validator validator) {
|
||||
return
|
||||
BinaryUnmarshaller.<AttributesImpl, T, String>builder()
|
||||
.map(AttributeMapper::map)
|
||||
.map(AttributesImpl::unmarshal)
|
||||
.map("application/json", Json.umarshaller(type)::unmarshal)
|
||||
.next()
|
||||
.map(ExtensionMapper::map)
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
.next()
|
||||
.builder(CloudEventBuilder.<T>builder().withValidator(validator)::build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @param typeOfData The type reference to use for 'data' unmarshal
|
||||
* @return A step to supply the headers, payload and to unmarshal
|
||||
* @see StructuredUnmarshaller
|
||||
*/
|
||||
public static <T> HeadersStep<AttributesImpl, T, String>
|
||||
structured(Class<T> typeOfData) {
|
||||
return structured(typeOfData, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @param typeOfData The type reference to use for 'data' unmarshal
|
||||
* @param validator Provided instance of a {@link Validator}
|
||||
* @return A step to supply the headers, payload and to unmarshal
|
||||
* @see StructuredUnmarshaller
|
||||
*/
|
||||
public static <T> HeadersStep<AttributesImpl, T, String>
|
||||
structured(Class<T> typeOfData, Validator validator) {
|
||||
|
||||
return
|
||||
StructuredUnmarshaller.<AttributesImpl, T, String>
|
||||
builder()
|
||||
.map(ExtensionMapper::map)
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
.next()
|
||||
.map((payload, extensions) -> {
|
||||
CloudEventImpl<T> event =
|
||||
Json.<CloudEventImpl<T>>
|
||||
decodeValue(payload, CloudEventImpl.class, typeOfData);
|
||||
|
||||
CloudEventBuilder<T> builder =
|
||||
CloudEventBuilder.<T>builder(event);
|
||||
|
||||
extensions.get().forEach(extension -> {
|
||||
builder.withExtension(extension);
|
||||
});
|
||||
|
||||
return builder.withValidator(validator).build();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v1;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.fun.ExtensionFormatAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Accessor {
|
||||
|
||||
/**
|
||||
* To get access the set of {@link ExtensionFormat} inside the
|
||||
* event.
|
||||
*
|
||||
* <br>
|
||||
* <br>
|
||||
* This method follow the signature of
|
||||
* {@link ExtensionFormatAccessor#extensionsOf(CloudEvent)}
|
||||
*
|
||||
* @param cloudEvent
|
||||
* @throws IllegalArgumentException When argument is not an instance
|
||||
* of {@link CloudEventImpl}
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static <A extends Attributes, T> Collection<ExtensionFormat>
|
||||
extensionsOf(CloudEvent<A, T> cloudEvent) {
|
||||
Objects.requireNonNull(cloudEvent);
|
||||
|
||||
if(cloudEvent instanceof CloudEventImpl) {
|
||||
CloudEventImpl impl = (CloudEventImpl)cloudEvent;
|
||||
return impl.getExtensionsFormats();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid instance type: "
|
||||
+ cloudEvent.getClass());
|
||||
}
|
||||
}
|
|
@ -1,208 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v1;
|
||||
|
||||
import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import io.cloudevents.Attributes;
|
||||
import io.cloudevents.json.ZonedDateTimeDeserializer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 1.0
|
||||
*/
|
||||
@JsonInclude(value = Include.NON_ABSENT)
|
||||
public class AttributesImpl implements Attributes {
|
||||
|
||||
@NotBlank
|
||||
private final String id;
|
||||
|
||||
@NotNull
|
||||
private final URI source;
|
||||
|
||||
@NotBlank
|
||||
@Pattern(regexp = "1\\.0")
|
||||
private final String specversion;
|
||||
|
||||
@NotBlank
|
||||
private final String type;
|
||||
|
||||
private final String datacontenttype;
|
||||
|
||||
private final URI dataschema;
|
||||
|
||||
@Size(min = 1)
|
||||
private final String subject;
|
||||
|
||||
@JsonDeserialize(using = ZonedDateTimeDeserializer.class)
|
||||
private final ZonedDateTime time;
|
||||
|
||||
public AttributesImpl(String id, URI source, String specversion,
|
||||
String type, String datacontenttype,
|
||||
URI dataschema, String subject, ZonedDateTime time) {
|
||||
|
||||
this.id = id;
|
||||
this.source = source;
|
||||
this.specversion = specversion;
|
||||
this.type = type;
|
||||
this.datacontenttype = datacontenttype;
|
||||
this.dataschema = dataschema;
|
||||
this.subject = subject;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getMediaType() {
|
||||
return getDatacontenttype();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public URI getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public String getSpecversion() {
|
||||
return specversion;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Optional<String> getDatacontenttype() {
|
||||
return Optional.ofNullable(datacontenttype);
|
||||
}
|
||||
|
||||
public Optional<URI> getDataschema() {
|
||||
return Optional.ofNullable(dataschema);
|
||||
}
|
||||
|
||||
public Optional<String> getSubject() {
|
||||
return Optional.ofNullable(subject);
|
||||
}
|
||||
|
||||
public Optional<ZonedDateTime> getTime() {
|
||||
return Optional.ofNullable(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AttibutesImpl [id=" + id + ", source=" + source
|
||||
+ ", specversion=" + specversion + ", type=" + type
|
||||
+ ", datacontenttype=" + datacontenttype + ", dataschema="
|
||||
+ dataschema + ", subject=" + subject
|
||||
+ ", time=" + time + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the Jackson framework to unmarshall.
|
||||
*/
|
||||
@JsonCreator
|
||||
public static AttributesImpl build(
|
||||
@JsonProperty("id") String id,
|
||||
@JsonProperty("source") URI source,
|
||||
@JsonProperty("specversion") String specversion,
|
||||
@JsonProperty("type") String type,
|
||||
@JsonProperty("datacontenttype") String datacontenttype,
|
||||
@JsonProperty("dataschema") URI dataschema,
|
||||
@JsonProperty("subject") String subject,
|
||||
@JsonProperty("time") ZonedDateTime time) {
|
||||
|
||||
return new AttributesImpl(id, source, specversion, type,
|
||||
datacontenttype, dataschema, subject, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the marshaller instance to marshall {@link AttributesImpl} as
|
||||
* a {@link Map} of strings
|
||||
*/
|
||||
public static Map<String, String> marshal(AttributesImpl attributes) {
|
||||
Objects.requireNonNull(attributes);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
||||
result.put(ContextAttributes.id.name(),
|
||||
attributes.getId());
|
||||
result.put(ContextAttributes.source.name(),
|
||||
attributes.getSource().toString());
|
||||
result.put(ContextAttributes.specversion.name(),
|
||||
attributes.getSpecversion());
|
||||
result.put(ContextAttributes.type.name(),
|
||||
attributes.getType());
|
||||
|
||||
attributes.getDatacontenttype().ifPresent(dct -> result.put(ContextAttributes.datacontenttype.name(), dct));
|
||||
attributes.getDataschema().ifPresent(dataschema -> result.put(ContextAttributes.dataschema.name(),
|
||||
dataschema.toString()));
|
||||
attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.subject.name(), subject));
|
||||
attributes.getTime().ifPresent(time -> result.put(ContextAttributes.time.name(),
|
||||
time.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The attribute unmarshaller for the binary format, that receives a
|
||||
* {@code Map} with attributes names as String and value as String.
|
||||
*/
|
||||
public static AttributesImpl unmarshal(Map<String, String> attributes) {
|
||||
String type = attributes.get(ContextAttributes.type.name());
|
||||
ZonedDateTime time =
|
||||
Optional.ofNullable(attributes.get(ContextAttributes.time.name()))
|
||||
.map((t) -> ZonedDateTime.parse(t,
|
||||
ISO_ZONED_DATE_TIME))
|
||||
.orElse(null);
|
||||
|
||||
String specversion = attributes.get(ContextAttributes.specversion.name());
|
||||
URI source = URI.create(attributes.get(ContextAttributes.source.name()));
|
||||
|
||||
URI dataschema =
|
||||
Optional.ofNullable(attributes.get(ContextAttributes.dataschema.name()))
|
||||
.map(URI::create)
|
||||
.orElse(null);
|
||||
|
||||
String id = attributes.get(ContextAttributes.id.name());
|
||||
|
||||
String datacontenttype =
|
||||
attributes.get(ContextAttributes.datacontenttype.name());
|
||||
|
||||
String subject = attributes.get(ContextAttributes.subject.name());
|
||||
|
||||
return AttributesImpl.build(id, source, specversion, type,
|
||||
datacontenttype, dataschema, subject, time);
|
||||
}
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
package io.cloudevents.v1;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.fun.EventBuilder;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 1.0
|
||||
*/
|
||||
public class CloudEventBuilder<T> implements
|
||||
EventBuilder<T, AttributesImpl> {
|
||||
|
||||
private CloudEventBuilder() {}
|
||||
|
||||
private static Validator VALIDATOR;
|
||||
|
||||
public static final String SPEC_VERSION = "1.0";
|
||||
private static final String MESSAGE_SEPARATOR = ", ";
|
||||
private static final String MESSAGE = "'%s' %s";
|
||||
private static final String ERR_MESSAGE = "invalid payload: %s";
|
||||
|
||||
private String id;
|
||||
private URI source;
|
||||
|
||||
private String type;
|
||||
private String datacontenttype;
|
||||
private URI dataschema;
|
||||
private String subject;
|
||||
private ZonedDateTime time;
|
||||
|
||||
private T data;
|
||||
private byte[] dataBase64;
|
||||
|
||||
private final Set<ExtensionFormat> extensions = new HashSet<>();
|
||||
|
||||
private Validator validator;
|
||||
|
||||
private static Validator getValidator() {
|
||||
if(null== VALIDATOR) {
|
||||
VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
}
|
||||
return VALIDATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a brand new builder instance
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
public static <T> CloudEventBuilder<T> builder() {
|
||||
return new CloudEventBuilder<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder with base event to copy attributes
|
||||
* @param <T> The 'data' type
|
||||
* @param base The base event to copy attributes
|
||||
*/
|
||||
public static <T> CloudEventBuilder<T> builder(
|
||||
CloudEvent<AttributesImpl, T> base) {
|
||||
Objects.requireNonNull(base);
|
||||
|
||||
CloudEventBuilder<T> result = new CloudEventBuilder<>();
|
||||
|
||||
AttributesImpl attributes = base.getAttributes();
|
||||
|
||||
result
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getDataschema().ifPresent(result::withDataschema);
|
||||
attributes.getDatacontenttype().ifPresent(result::withDataContentType);
|
||||
attributes.getSubject().ifPresent(result::withSubject);
|
||||
attributes.getTime().ifPresent(result::withTime);
|
||||
Accessor.extensionsOf(base).forEach(result::withExtension);
|
||||
base.getData().ifPresent(result::withData);
|
||||
if(base.getDataBase64() != null) {
|
||||
result.withDataBase64(base.getDataBase64());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent<AttributesImpl, T> build(T data,
|
||||
AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions) {
|
||||
|
||||
CloudEventBuilder<T> builder = CloudEventBuilder.<T>builder()
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent(builder::withTime);
|
||||
attributes.getDataschema().ifPresent(builder::withDataschema);
|
||||
attributes.getDatacontenttype().ifPresent(builder::withDataContentType);
|
||||
attributes.getSubject().ifPresent(builder::withSubject);
|
||||
extensions.forEach(builder::withExtension);
|
||||
|
||||
return builder
|
||||
.withData(data)
|
||||
.withDataBase64(dataBase64)
|
||||
.withValidator(validator)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return An new {@link CloudEvent} immutable instance
|
||||
* @throws IllegalStateException When there are specification constraints
|
||||
* violations
|
||||
*/
|
||||
public CloudEventImpl<T> build() {
|
||||
|
||||
AttributesImpl attributes = new AttributesImpl(id, source, SPEC_VERSION, type,
|
||||
datacontenttype, dataschema, subject, time);
|
||||
|
||||
CloudEventImpl<T> cloudEvent;
|
||||
if(data != null) {
|
||||
cloudEvent = new CloudEventImpl<>(attributes, data, extensions);
|
||||
} else {
|
||||
cloudEvent = new CloudEventImpl<>(attributes, dataBase64, extensions);
|
||||
}
|
||||
|
||||
if(validator == null) {
|
||||
validator = getValidator();
|
||||
}
|
||||
Set<ConstraintViolation<Object>> violations = new HashSet<>();
|
||||
violations.addAll(validator.validate(cloudEvent));
|
||||
violations.addAll(validator.validate(cloudEvent.getAttributes()));
|
||||
|
||||
final String errs =
|
||||
violations.stream()
|
||||
.map(v -> format(MESSAGE, v.getPropertyPath(), v.getMessage()))
|
||||
.collect(Collectors.joining(MESSAGE_SEPARATOR));
|
||||
|
||||
if(!errs.trim().isEmpty()) {
|
||||
throw new IllegalStateException(format(ERR_MESSAGE, errs));
|
||||
}
|
||||
|
||||
return cloudEvent;
|
||||
}
|
||||
|
||||
|
||||
public CloudEventBuilder<T> withId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSource(URI source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withDataschema(URI dataschema) {
|
||||
this.dataschema = dataschema;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withDataContentType(
|
||||
String datacontenttype) {
|
||||
this.datacontenttype = datacontenttype;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSubject(
|
||||
String subject) {
|
||||
this.subject = subject;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withTime(ZonedDateTime time) {
|
||||
this.time = time;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withData(T data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withDataBase64(byte[] dataBase64) {
|
||||
this.dataBase64 = dataBase64;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withExtension(ExtensionFormat extension) {
|
||||
this.extensions.add(extension);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v1;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.extensions.InMemoryFormat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 1.0
|
||||
*/
|
||||
@JsonInclude(value = JsonInclude.Include.NON_ABSENT)
|
||||
public class CloudEventImpl<T> implements CloudEvent<AttributesImpl, T> {
|
||||
|
||||
@NotNull
|
||||
@JsonIgnore
|
||||
private final AttributesImpl attributes;
|
||||
|
||||
private final T data;
|
||||
|
||||
//To use with json binary data
|
||||
private final byte[] dataBase64;
|
||||
|
||||
@NotNull
|
||||
private final Map<String, Object> extensions;
|
||||
|
||||
private final Set<ExtensionFormat> extensionsFormats;
|
||||
|
||||
CloudEventImpl(AttributesImpl attributes, byte[] dataBase64,
|
||||
Set<ExtensionFormat> extensions){
|
||||
this(attributes, extensions, null, dataBase64);
|
||||
}
|
||||
|
||||
CloudEventImpl(AttributesImpl attributes, T data,
|
||||
Set<ExtensionFormat> extensions){
|
||||
this(attributes, extensions, data, null);
|
||||
}
|
||||
|
||||
private CloudEventImpl(AttributesImpl attributes, Set<ExtensionFormat> extensions, T data, byte[] dataBase64){
|
||||
this.attributes = attributes;
|
||||
this.extensions = extensions.stream()
|
||||
.map(ExtensionFormat::memory)
|
||||
.collect(Collectors.toMap(InMemoryFormat::getKey,
|
||||
InMemoryFormat::getValue));
|
||||
this.data = data;
|
||||
this.dataBase64 = dataBase64;
|
||||
this.extensionsFormats = extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the {@link Accessor} to access the set of {@link ExtensionFormat}
|
||||
*/
|
||||
Set<ExtensionFormat> getExtensionsFormats() {
|
||||
return extensionsFormats;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@JsonUnwrapped
|
||||
public AttributesImpl getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> getData() {
|
||||
return Optional.ofNullable(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonProperty("data_base64")
|
||||
public byte[] getDataBase64() {
|
||||
return dataBase64;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> getExtensions() {
|
||||
return Collections.unmodifiableMap(extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* The unique method that allows mutation. Used by
|
||||
* Jackson Framework to inject the extensions.
|
||||
*
|
||||
* @param name Extension name
|
||||
* @param value Extension value
|
||||
*/
|
||||
@JsonAnySetter
|
||||
void addExtension(String name, Object value) {
|
||||
extensions.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the Jackson Framework to unmarshall.
|
||||
*/
|
||||
@JsonCreator
|
||||
public static <T> CloudEventImpl<T> build(
|
||||
@JsonProperty("id") String id,
|
||||
@JsonProperty("source") URI source,
|
||||
@JsonProperty("type") String type,
|
||||
@JsonProperty("datacontenttype") String datacontenttype,
|
||||
@JsonProperty("dataschema") URI dataschema,
|
||||
@JsonProperty("subject") String subject,
|
||||
@JsonProperty("time") ZonedDateTime time,
|
||||
@JsonProperty("data") T data,
|
||||
@JsonProperty("data_base64") byte[] dataBase64){
|
||||
|
||||
return CloudEventBuilder.<T>builder()
|
||||
.withId(id)
|
||||
.withSource(source)
|
||||
.withType(type)
|
||||
.withTime(time)
|
||||
.withDataschema(dataschema)
|
||||
.withDataContentType(datacontenttype)
|
||||
.withData(data)
|
||||
.withDataBase64(dataBase64)
|
||||
.withSubject(subject)
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v1;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 1.0
|
||||
*/
|
||||
public enum ContextAttributes {
|
||||
|
||||
id,
|
||||
source,
|
||||
specversion,
|
||||
type,
|
||||
datacontenttype,
|
||||
dataschema,
|
||||
subject,
|
||||
time;
|
||||
|
||||
public static final List<String> VALUES =
|
||||
Arrays.stream(ContextAttributes.values())
|
||||
.map(Enum::name)
|
||||
.collect(Collectors.toList());
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v1.http;
|
||||
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.cloudevents.fun.BinaryFormatAttributeMapper;
|
||||
import io.cloudevents.v1.ContextAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 1.0
|
||||
*/
|
||||
public class AttributeMapper {
|
||||
private AttributeMapper() {}
|
||||
|
||||
static final String HEADER_PREFIX = "ce-";
|
||||
|
||||
/**
|
||||
* Following the signature of {@link BinaryFormatAttributeMapper#map(Map)}
|
||||
* @param headers Map of HTTP request
|
||||
* @return Map with spec attributes and values without parsing
|
||||
* @see ContextAttributes
|
||||
*/
|
||||
public static Map<String, String> map(final Map<String, Object> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
final AtomicReference<Optional<Entry<String, Object>>> ct =
|
||||
new AtomicReference<>();
|
||||
|
||||
ct.set(Optional.empty());
|
||||
|
||||
Map<String, String> result = headers.entrySet()
|
||||
.stream()
|
||||
.filter(header -> null!= header.getValue())
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.toLowerCase(Locale.US), header.getValue()))
|
||||
.peek(header -> {
|
||||
if("content-type".equals(header.getKey())) {
|
||||
ct.set(Optional.ofNullable(header));
|
||||
}
|
||||
})
|
||||
.filter(header -> header.getKey().startsWith(HEADER_PREFIX))
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.substring(HEADER_PREFIX.length()), header.getValue()))
|
||||
.map(header -> new SimpleEntry<>(header.getKey(),
|
||||
header.getValue().toString()))
|
||||
.collect(toMap(Entry::getKey, Entry::getValue));
|
||||
|
||||
ct.get().ifPresent(contentType -> {
|
||||
result.put(ContextAttributes.datacontenttype.name(),
|
||||
contentType.getValue().toString());
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package io.cloudevents.v1.http;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.cloudevents.fun.FormatExtensionMapper;
|
||||
import io.cloudevents.v1.ContextAttributes;
|
||||
import io.cloudevents.v1.http.AttributeMapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
* @version 1.0
|
||||
*/
|
||||
public class ExtensionMapper {
|
||||
private ExtensionMapper() {}
|
||||
|
||||
private static final List<String> RESERVED_HEADERS =
|
||||
ContextAttributes.VALUES.stream()
|
||||
.map(attribute -> AttributeMapper
|
||||
.HEADER_PREFIX + attribute)
|
||||
.collect(Collectors.toList());
|
||||
static {
|
||||
RESERVED_HEADERS.add("content-type");
|
||||
};
|
||||
|
||||
/**
|
||||
* Following the signature of {@link FormatExtensionMapper}
|
||||
* @param headers The HTTP headers
|
||||
* @return The potential extensions without parsing
|
||||
*/
|
||||
public static Map<String, String> map(Map<String, Object> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
// remove all reserved words and the remaining may be extensions
|
||||
return
|
||||
headers.entrySet()
|
||||
.stream()
|
||||
.filter(header -> null!= header.getValue())
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.toLowerCase(Locale.US), header.getValue().toString()))
|
||||
.filter(header -> !RESERVED_HEADERS.contains(header.getKey()))
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/**
|
||||
* Copyright 2019 The CloudEvents Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES 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.v1.http;
|
||||
|
||||
import static io.cloudevents.v1.http.AttributeMapper.HEADER_PREFIX;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.cloudevents.fun.FormatHeaderMapper;
|
||||
import io.cloudevents.v1.ContextAttributes;
|
||||
import io.cloudevents.v1.http.AttributeMapper;
|
||||
import io.cloudevents.v1.http.ExtensionMapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public class HeaderMapper {
|
||||
private HeaderMapper() {}
|
||||
|
||||
private static final String HTTP_CONTENT_TYPE = "Content-Type";
|
||||
|
||||
/**
|
||||
* Following the signature of {@link FormatHeaderMapper}
|
||||
* @param attributes The map of attributes created by {@link AttributeMapper}
|
||||
* @param extensions The map of extensions created by {@link ExtensionMapper}
|
||||
* @return The map of HTTP Headers
|
||||
*/
|
||||
public static Map<String, String> map(Map<String, String> attributes,
|
||||
Map<String, String> extensions) {
|
||||
Objects.requireNonNull(attributes);
|
||||
Objects.requireNonNull(extensions);
|
||||
|
||||
Map<String, String> result = attributes.entrySet()
|
||||
.stream()
|
||||
.filter(attribute -> null!= attribute.getValue())
|
||||
.map(header -> new SimpleEntry<>(header.getKey()
|
||||
.toLowerCase(Locale.US), header.getValue()))
|
||||
.filter(header -> !header.getKey()
|
||||
.equals(ContextAttributes.datacontenttype.name()))
|
||||
.map(header -> new SimpleEntry<>(HEADER_PREFIX + header.getKey(),
|
||||
header.getValue()))
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
|
||||
result.putAll(
|
||||
extensions.entrySet()
|
||||
.stream()
|
||||
.filter(extension -> null != extension.getValue())
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue))
|
||||
);
|
||||
|
||||
Optional.ofNullable(attributes
|
||||
.get(ContextAttributes.datacontenttype.name()))
|
||||
.ifPresent((dct) -> {
|
||||
result.put(HTTP_CONTENT_TYPE, dct);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue