diff --git a/.github/workflows/branch.yaml b/.github/workflows/branch.yaml index 4c5f60743..06c75437b 100644 --- a/.github/workflows/branch.yaml +++ b/.github/workflows/branch.yaml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ 8, 11 ] + java: [ 8, 11, 17 ] steps: - uses: actions/checkout@v2 - name: Setup java @@ -20,8 +20,9 @@ jobs: with: java-version: ${{ matrix.java }} - run: | - mvn clean install -DskipTests -B - mvn verify -B + ./mvnw clean install -DskipTests -B + ./mvnw verify -B + deploy: runs-on: ubuntu-latest name: Deploy @@ -38,7 +39,7 @@ jobs: gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Publish to Apache Maven Central - run: mvn clean deploy -Drelease -DskipTests -B + run: ./mvnw clean deploy -Drelease -DskipTests -B env: MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} diff --git a/.github/workflows/bump.yaml b/.github/workflows/bump.yaml index 3636cb5b2..86ca4215b 100644 --- a/.github/workflows/bump.yaml +++ b/.github/workflows/bump.yaml @@ -19,7 +19,7 @@ jobs: with: java-version: 8 - name: Bump version using Maven - run: 'mvn versions:set -DnewVersion=$NEW_VERSION -DgenerateBackupPoms=false -B' + 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+[a-zA-Z0-9.-]*<\/version>+$NEW_VERSION+g" {} +' diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 57780b56f..2f808c92b 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -22,5 +22,6 @@ jobs: java-version: ${{ matrix.java }} distribution: 'temurin' - run: | - mvn clean install -DskipTests -B - mvn verify -B + ./mvnw clean install -DskipTests -B + ./mvnw verify -B + ./mvnw javadoc:javadoc diff --git a/.gitignore b/.gitignore index 4320cfaa4..9cc77463c 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,5 @@ _site/ # MacOS *.DS_Store +/http/restful-ws-jakarta/src/main/* + diff --git a/amqp/pom.xml b/amqp/pom.xml index cff9ac2d9..e81b65c21 100644 --- a/amqp/pom.xml +++ b/amqp/pom.xml @@ -6,7 +6,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT cloudevents-amqp-proton diff --git a/api/pom.xml b/api/pom.xml index 885668ba0..9a8331491 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -24,7 +24,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT cloudevents-api diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml index 5439585d5..4452def8e 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -21,7 +21,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT cloudevents-benchmarks diff --git a/bom/pom.xml b/bom/pom.xml index c7b6eaaff..041c58e10 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -22,7 +22,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT cloudevents-bom diff --git a/core/pom.xml b/core/pom.xml index e037daa15..294171641 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -22,7 +22,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT cloudevents-core diff --git a/docs/amqp-proton.md b/docs/amqp-proton.md index f6a456c04..3795435cb 100644 --- a/docs/amqp-proton.md +++ b/docs/amqp-proton.md @@ -18,7 +18,7 @@ binding for CloudEvents: io.cloudevents cloudevents-amqp-proton - 2.3.0 + 2.5.0 ``` diff --git a/docs/api.md b/docs/api.md index 1de08596f..7bd8b68fa 100644 --- a/docs/api.md +++ b/docs/api.md @@ -17,7 +17,7 @@ For Maven based projects, use the following dependency: io.cloudevents cloudevents-api - 2.3.0 + 2.5.0 ``` diff --git a/docs/core.md b/docs/core.md index fcafac13d..8934428dc 100644 --- a/docs/core.md +++ b/docs/core.md @@ -16,7 +16,7 @@ For Maven based projects, use the following dependency: io.cloudevents cloudevents-core - 2.3.0 + 2.5.0 ``` diff --git a/docs/http-basic.md b/docs/http-basic.md index f2cb67aa2..526a49c07 100644 --- a/docs/http-basic.md +++ b/docs/http-basic.md @@ -27,7 +27,7 @@ HTTP Transport: io.cloudevents cloudevents-http-basic - 2.3.0 + 2.5.0 ``` diff --git a/docs/http-jakarta-restful-ws-jakarta.md b/docs/http-jakarta-restful-ws-jakarta.md new file mode 100644 index 000000000..228867994 --- /dev/null +++ b/docs/http-jakarta-restful-ws-jakarta.md @@ -0,0 +1,137 @@ +--- +title: CloudEvents HTTP Jakarta EE 9+ - Jakarta RESTful Web Services +nav_order: 5 +--- + +# HTTP Protocol Binding for Jakarta EE 9+ - Jakarta RESTful Web Services + +[![Javadocs](https://www.javadoc.io/badge/io.cloudevents/cloudevents-http-restful-ws.svg?color=green)](https://www.javadoc.io/doc/io.cloudevents/cloudevents-http-restful-ws) + +For Maven based projects, use the following to configure the CloudEvents Jakarta +RESTful Web Services Binding for Jakarta EE 9+: + +```xml + + io.cloudevents + cloudevents-http-restful-ws-jakarta + 2.5.0 + +``` + +This integration is tested with Jersey (Requires JDK11 or higher), RestEasy & Microprofile Liberty. + +#### * Before using this package ensure your web framework does support the `jakarta.*` namespace. + +## Receiving CloudEvents + +You need to configure the `CloudEventsProvider` to enable +marshalling/unmarshalling of CloudEvents. + +Below is a sample on how to read and write CloudEvents: + +```java +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; + +@Path("/") +public class EventReceiverResource { + + + + @GET + @Path("getMinEvent") + public CloudEvent getMinEvent() { + return CloudEventBuilder.v1() + .withId("hello") + .withType("example.vertx") + .withSource(URI.create("http://localhost")) + .build(); + } + + // Return the CloudEvent using the HTTP binding structured encoding + @GET + @Path("getStructuredEvent") + @StructuredEncoding("application/cloudevents+csv") + public CloudEvent getStructuredEvent() { + return CloudEventBuilder.v1() + .withId("hello") + .withType("example.vertx") + .withSource(URI.create("http://localhost")) + .build(); + } + + @POST + @Path("postEventWithoutBody") + public Response postEvent(CloudEvent inputEvent) { + // Handle the event + return Response.ok().build(); + } +} +``` + +## Sending CloudEvents + +You need to configure the `CloudEventsProvider` to enable +marshalling/unmarshalling of CloudEvents. + +Below is a sample on how to use the client to send a CloudEvent: + +```java +import io.cloudevents.CloudEvent; +import io.cloudevents.http.restful.ws.CloudEventsProvider; + +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; + +public class CloudEventSender { + + public Response sendEvent(WebTarget target, CloudEvent event) { + return target + .path("postEvent") + .request() + .buildPost(Entity.entity(event, CloudEventsProvider.CLOUDEVENT_TYPE)) + .invoke(); + } + + public Response sendEventAsStructured(WebTarget target, CloudEvent event) { + return target + .path("postEvent") + .request() + .buildPost(Entity.entity(event, "application/cloudevents+json")) + .invoke(); + } + + public CloudEvent getEvent(WebTarget target) { + Response response = target + .path("getEvent") + .request() + .buildGet() + .invoke(); + + return response.readEntity(CloudEvent.class); + } +} +``` + +## Migrating EE 8 applications to EE 9+ +The main change between Jakarta EE 8 and Jakarta EE 9 and future versions is the changing of the `javax.` to `jakarta.` namespaces used by key packages such as `jakarta.ws.rs-api` which provides the restful-ws API. + +This change largely impacts only `import` statements it does filter down to dependencies such as this. + +### Application migration +For application migration we would recommend reviewing materials available from https://jakarta.ee/resources/#documentation as a starting point. + +### CloudEvents Dependency +To migrate to use EE 9+ supported package - replace `cloudevents-http-restful-ws` with `cloudevents-http-restful-ws-jakarta` and ensure the version is a minimum of `2.5.0-SNAPSHOT` + +## Examples + +- [Microprofile and Liberty](https://github.com/cloudevents/sdk-java/tree/master/examples/restful-ws-micropofile-liberty) + diff --git a/docs/http-jakarta-restful-ws.md b/docs/http-jakarta-restful-ws.md index a7f08fefb..bfd72da71 100644 --- a/docs/http-jakarta-restful-ws.md +++ b/docs/http-jakarta-restful-ws.md @@ -3,18 +3,18 @@ title: CloudEvents HTTP Jakarta RESTful Web Services nav_order: 5 --- -# HTTP Protocol Binding for Jakarta RESTful Web Services +# HTTP Protocol Binding for Jakarta EE8 - RESTful Web Services [![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-http-restful-ws.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-http-restful-ws) For Maven based projects, use the following to configure the CloudEvents Jakarta -RESTful Web Services Binding: +RESTful Web Services Binding for Jakarta EE 8: ```xml io.cloudevents cloudevents-http-restful-ws - 2.3.0 + 2.5.0 ``` diff --git a/docs/http-vertx.md b/docs/http-vertx.md index bdecc9a79..39d454a82 100644 --- a/docs/http-vertx.md +++ b/docs/http-vertx.md @@ -14,7 +14,7 @@ HTTP Transport: io.cloudevents cloudevents-http-vertx - 2.3.0 + 2.5.0 ``` diff --git a/docs/index.md b/docs/index.md index ea4552f3a..07664c2ee 100644 --- a/docs/index.md +++ b/docs/index.md @@ -62,7 +62,8 @@ receive CloudEvents, check out the dedicated pages: - [AMQP using Proton](amqp-proton.md) - [HTTP using Vert.x](http-vertx.md) -- [HTTP using Jakarta Restful WS](http-jakarta-restful-ws.md) +- [HTTP using Jakarta EE 8 - Jakarta Restful WS](http-jakarta-restful-ws.md) +- [HTTP using Jakarta EE 9+ - Jakarta Restful WS](http-jakarta-restful-ws-jakarta.md) - [HTTP using Spring](spring.md) - [HTTP using Jackson](json-jackson.md) - [Kafka](kafka.md) @@ -98,7 +99,9 @@ a different feature from the different sub specs of - [`cloudevents-http-vertx`] Implementation of [HTTP Protocol Binding] with [Vert.x Core](https://vertx.io/) - [`cloudevents-http-restful-ws`] Implementation of [HTTP Protocol Binding] - for [Jakarta Restful WS](https://jakarta.ee/specifications/restful-ws/) + for [Jakarta EE 8 Restful WS](https://jakarta.ee/specifications/restful-ws/2.1/) +- [`cloudevents-http-restful-ws-jakarta`] Implementation of [HTTP Protocol Binding] + for [Jakarta EE 9+ Restful WS](https://jakarta.ee/specifications/restful-ws/) - [`cloudevents-http-basic`] Generic implementation of [HTTP Protocol Binding], primarily intended for integrators - [`cloudevents-kafka`] Implementation of [Kafka Protocol Binding] @@ -123,6 +126,7 @@ You can look at the latest published artifacts on [`cloudevents-http-vertx`]: https://github.com/cloudevents/sdk-java/tree/master/http/vertx [`cloudevents-http-basic`]: https://github.com/cloudevents/sdk-java/tree/master/http/basic [`cloudevents-http-restful-ws`]: https://github.com/cloudevents/sdk-java/tree/master/http/restful-ws +[`cloudevents-http-restful-ws-jakarta`]: https://github.com/cloudevents/sdk-java/tree/master/http/restful-ws-jakarta [`cloudevents-kafka`]: https://github.com/cloudevents/sdk-java/tree/master/kafka [`cloudevents-amqp-proton`]: https://github.com/cloudevents/sdk-java/tree/master/amqp [`cloudevents-spring`]: https://github.com/cloudevents/sdk-java/tree/master/spring diff --git a/docs/json-jackson.md b/docs/json-jackson.md index 6bb2a7c6a..6daec987b 100644 --- a/docs/json-jackson.md +++ b/docs/json-jackson.md @@ -17,7 +17,7 @@ For Maven based projects, use the following dependency: io.cloudevents cloudevents-json-jackson - 2.3.0 + 2.5.0 ``` diff --git a/docs/kafka.md b/docs/kafka.md index bcdeb80ae..ea353fd9e 100644 --- a/docs/kafka.md +++ b/docs/kafka.md @@ -17,7 +17,7 @@ For Maven based projects, use the following to configure the io.cloudevents cloudevents-kafka - 2.3.0 + 2.5.0 ``` diff --git a/docs/protobuf.md b/docs/protobuf.md index 64d7c4318..93e9578f6 100644 --- a/docs/protobuf.md +++ b/docs/protobuf.md @@ -17,7 +17,7 @@ For Maven based projects, use the following dependency: io.cloudevents cloudevents-protobuf - 2.3.0 + 2.5.0 ``` diff --git a/docs/spring.md b/docs/spring.md index b9dab8ed4..b0f2ef7d5 100644 --- a/docs/spring.md +++ b/docs/spring.md @@ -17,7 +17,7 @@ For Maven based projects, use the following dependency: io.cloudevents cloudevents-spring - 2.3.0 + 2.5.0 ``` diff --git a/examples/amqp-proton/pom.xml b/examples/amqp-proton/pom.xml index 5b888ae31..175ecbb8e 100644 --- a/examples/amqp-proton/pom.xml +++ b/examples/amqp-proton/pom.xml @@ -3,7 +3,7 @@ cloudevents-examples io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 diff --git a/examples/basic-http/pom.xml b/examples/basic-http/pom.xml index 27b162cda..458aad805 100644 --- a/examples/basic-http/pom.xml +++ b/examples/basic-http/pom.xml @@ -21,7 +21,7 @@ cloudevents-examples io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 diff --git a/examples/kafka/pom.xml b/examples/kafka/pom.xml index f20ca8320..9f6bf4e55 100644 --- a/examples/kafka/pom.xml +++ b/examples/kafka/pom.xml @@ -5,7 +5,7 @@ cloudevents-examples io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 diff --git a/examples/pom.xml b/examples/pom.xml index 9a7ee82be..13fec11b2 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -5,7 +5,7 @@ cloudevents-parent io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 @@ -21,6 +21,7 @@ kafka restful-ws-quarkus + restful-ws-microprofile-liberty vertx basic-http restful-ws-spring-boot diff --git a/examples/restful-ws-microprofile-liberty/README.md b/examples/restful-ws-microprofile-liberty/README.md new file mode 100644 index 000000000..a5a320222 --- /dev/null +++ b/examples/restful-ws-microprofile-liberty/README.md @@ -0,0 +1,140 @@ +# Cloudevents Restful WS Microprofile Example + +This project uses Microprofile 5.0 with OpenLiberty + +If you would like to know more about Microprofile go to https://microprofile.io + +This Example uses Jakarta EE9 features as such the top level namespace of the `ws-api` packages has changed from `javax` to `jakarta` and uses the `cloudevents-http-restful-ws-jakarta` artifact. + +## Build and Execution + +### Running the application in dev mode + +You can run your application in dev mode that enables live coding using: +``` +mvn liberty:dev +``` + +### Packaging and running the application + +To Package and run as a minimum jar without a full OpenLiberty server +``` +mvn liberty:package -Drunnable=mvn liberty:package -Dinclude=runnable +``` + +### Making requests against the server + +This sample application has a `/events` RESTful endpoint on the application `cloudevents-restful-ws-microprofile-example +the base application is available at `http://localhost:9080/cloudevents-restful-ws-microprofile-example/` + +There are three operations that can be performed: +#### GET /events +Returns a Cloud event with a payload containing a message + +```shell +curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events + +* Trying 127.0.0.1:9080... +* Connected to localhost (127.0.0.1) port 9080 (#0) +> GET /cloudevents-restful-ws-microprofile-example/events HTTP/1.1 +> Host: localhost:9080 +> User-Agent: curl/7.83.1 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 201 no-content +< Ce-Id: hello +< Ce-Source: http://localhost +< Ce-Specversion: 1.0 +< Ce-Type: example.http +< Content-Type: application/json +< Content-Language: en-GB +< Content-Length: 64 +< Date: Wed, 17 Aug 2022 14:01:50 GMT +< +{"message":"Welcome to this Cloudevents + Microprofile example"} +``` + +#### POST /events +POST a Cloudevent with a payload that is printed out in the server logs + +```shell +curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events \ +-H "Ce-Specversion: 1.0" \ +-H "Ce-Type: User" \ +-H "Ce-Source: io.cloudevents.examples/user" \ +-H "Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78" \ +-H "Content-Type: application/json" \ +-H "Ce-Subject: SUBJ-0001" \ +-d "hello" + +* Trying 127.0.0.1:9080... +* Connected to localhost (127.0.0.1) port 9080 (#0) +> POST /cloudevents-restful-ws-microprofile-example/events HTTP/1.1 +> Host: localhost:9080 +> User-Agent: curl/7.83.1 +> Accept: */* +> Ce-Specversion: 1.0 +> Ce-Type: User +> Ce-Source: io.cloudevents.examples/user +> Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78 +> Content-Type: application/json +> Ce-Subject: SUBJ-0001 +> Content-Length: 5 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 204 No Content +< Content-Language: en-GB +< Content-Length: 0 +< Date: Thu, 18 Aug 2022 13:33:03 GMT +< +* Connection #0 to host localhost left intact +``` +Server log statement +``` +[INFO] Received request providing a event with body hello +[INFO] CloudEvent{id='536808d3-88be-4077-9d7a-a3f162705f78', source=io.cloudevents.examples/user, type='User', datacontenttype='application/json', subject='SUBJ-0001', data=BytesCloudEventData{value=[104, 101, 108, 108, 111]}, extensions={}} +``` + +#### POST /events/echo +POST a Cloudevent with a payload and have it echoed back as a Cloudevent + +```shell +curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events/echo \ +-H "Ce-Specversion: 1.0" \ +-H "Ce-Type: User" \ +-H "Ce-Source: io.cloudevents.examples/user" \ +-H "Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78" \ +-H "Content-Type: application/json" \ +-H "Ce-Subject: SUBJ-0001" \ +-d "hello" + +* Trying 127.0.0.1:9080... +* Connected to localhost (127.0.0.1) port 9080 (#0) +> POST /cloudevents-restful-ws-microprofile-example/rest/events/echo HTTP/1.1 +> Host: localhost:9080 +> User-Agent: curl/7.83.1 +> Accept: */* +> Ce-Specversion: 1.0 +> Ce-Type: User +> Ce-Source: io.cloudevents.examples/user +> Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78 +> Content-Type: application/json +> Ce-Subject: SUBJ-0001 +> Content-Length: 5 +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< Ce-Id: echo +< Ce-Source: http://localhost +< Ce-Specversion: 1.0 +< Ce-Type: echo.http +< Content-Type: application/json +< Content-Language: en-GB +< Content-Length: 17 +< Date: Wed, 17 Aug 2022 12:57:59 GMT +< +{"echo": "hello"}* Connection #0 to host localhost left intact +``` + + diff --git a/examples/restful-ws-microprofile-liberty/pom.xml b/examples/restful-ws-microprofile-liberty/pom.xml new file mode 100644 index 000000000..38d83938c --- /dev/null +++ b/examples/restful-ws-microprofile-liberty/pom.xml @@ -0,0 +1,184 @@ + + + cloudevents-examples + io.cloudevents + 2.5.1-SNAPSHOT + ../ + + 4.0.0 + cloudevents-restful-ws-microprofile-liberty-example + war + + + 3.5.1 + cloudevents-microprofile + + + cloudeventsServer + 9080 + 9443 + + cloudeventsexperiments + ${project.build.directory}/${app.name}.zip + UTF-8 + + + + + runnable + + ${project.build.directory}/${app.name}.jar + runnable + + + + + + + org.eclipse.microprofile + microprofile + 5.0 + pom + provided + + + io.cloudevents + cloudevents-http-restful-ws-jakarta + ${project.parent.version} + + + io.projectreactor + reactor-core + 3.4.21 + + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + + false + pom.xml + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + copy-server-files + package + + copy-dependencies + + + server-snippet + true + + ${project.build.directory}/wlp/usr/servers/${wlpServerName}/configDropins/defaults + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.7 + + + copy-app + package + + copy-resources + + + ${project.build.directory}/wlp/usr/servers/${wlpServerName}/dropins + + + + ${project.build.directory} + + ${project.artifactId}-${project.version}.war + + + + + + + + + + io.openliberty.tools + liberty-maven-plugin + ${openliberty.maven.version} + + + package-server + package + + create + install-feature + deploy + package + + + target/wlp-package + + + + + runnable + ${final.name} + + ${final.name} + https://server.example.com + / + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.18.1 + + + integration-test + integration-test + + integration-test + + + + **/it/** + + + ${testServerHttpPort} + ${warContext} + ${running.bluemix} + ${cf.context.root} + + + + + verify-results + + verify + + + + + ${project.build.directory}/test-reports/it/failsafe-summary.xml + ${project.build.directory}/test-reports/it + + + + + diff --git a/examples/restful-ws-microprofile-liberty/src/main/java/io/cloudevents/examples/microprofile/CloudEventsApplication.java b/examples/restful-ws-microprofile-liberty/src/main/java/io/cloudevents/examples/microprofile/CloudEventsApplication.java new file mode 100644 index 000000000..fc212316f --- /dev/null +++ b/examples/restful-ws-microprofile-liberty/src/main/java/io/cloudevents/examples/microprofile/CloudEventsApplication.java @@ -0,0 +1,9 @@ +package io.cloudevents.examples.microprofile; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +@ApplicationPath("/") +public class CloudEventsApplication extends Application { + +} diff --git a/examples/restful-ws-microprofile-liberty/src/main/java/io/cloudevents/examples/microprofile/CloudEventsJaxrsService.java b/examples/restful-ws-microprofile-liberty/src/main/java/io/cloudevents/examples/microprofile/CloudEventsJaxrsService.java new file mode 100644 index 000000000..eef7fe882 --- /dev/null +++ b/examples/restful-ws-microprofile-liberty/src/main/java/io/cloudevents/examples/microprofile/CloudEventsJaxrsService.java @@ -0,0 +1,56 @@ +package io.cloudevents.examples.microprofile; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import jakarta.enterprise.context.RequestScoped; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import java.net.URI; +import java.nio.charset.StandardCharsets; + +@RequestScoped +@Path("events") +public class CloudEventsJaxrsService { + + + @GET + @Produces(MediaType.APPLICATION_JSON) + public CloudEvent retrieveEvent(){ + System.out.println("Received request for an event"); + return CloudEventBuilder.v1() + .withData("{\"message\":\"Welcome to this Cloudevents + Microprofile example\"}".getBytes()) + .withDataContentType("application/json") + .withId("hello") + .withType("example.http") + .withSource(URI.create("http://localhost")) + .build(); + } + + //Example Curl - curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events -H "Ce-Specversion: 1.0" -H "Ce-Type: User" -H "Ce-Source: io.cloudevents.examples/user" -H "Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78" -H "Content-Type: application/json" -H "Ce-Subject: SUBJ-0001" -d "hello" + + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Response postEvent(CloudEvent event){ + System.out.println("Received request providing a event with body "+ new String(event.getData().toBytes(), StandardCharsets.UTF_8)); + System.out.println(event); + return Response.noContent().build(); + } + + //Example Curl - curl -v http://localhost:9080/cloudevents-restful-ws-microprofile-example/events/echo -H "Ce-Specversion: 1.0" -H "Ce-Type: User" -H "Ce-Source: io.cloudevents.examples/user" -H "Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f78" -H "Content-Type: application/json" -H "Ce-Subject: SUBJ-0001" -d "hello" + + @POST + @Path("/echo") + @Consumes(MediaType.APPLICATION_JSON) + public CloudEvent echo(CloudEvent event){ + return CloudEventBuilder.v1() + .withData("application/json", ("{\"echo\": \"" + new String(event.getData().toBytes(),StandardCharsets.UTF_8) + "\"}").getBytes()) + .withId("echo") + .withType("echo.http") + .withSource(URI.create("http://localhost")) + .build(); + } + + +} diff --git a/examples/restful-ws-microprofile-liberty/src/main/liberty/config/server.xml b/examples/restful-ws-microprofile-liberty/src/main/liberty/config/server.xml new file mode 100644 index 000000000..eaac3901f --- /dev/null +++ b/examples/restful-ws-microprofile-liberty/src/main/liberty/config/server.xml @@ -0,0 +1,25 @@ + + + + + microProfile-5.0 + + + + + + + + + + + + + + + + + + diff --git a/examples/restful-ws-quarkus/pom.xml b/examples/restful-ws-quarkus/pom.xml index c5c2e691c..a627cd9aa 100644 --- a/examples/restful-ws-quarkus/pom.xml +++ b/examples/restful-ws-quarkus/pom.xml @@ -5,7 +5,7 @@ cloudevents-examples io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 cloudevents-restful-ws-quarkus-example diff --git a/examples/restful-ws-spring-boot/pom.xml b/examples/restful-ws-spring-boot/pom.xml index 9b5a25bd8..e9d3f0e09 100644 --- a/examples/restful-ws-spring-boot/pom.xml +++ b/examples/restful-ws-spring-boot/pom.xml @@ -5,7 +5,7 @@ cloudevents-examples io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 diff --git a/examples/spring-function/pom.xml b/examples/spring-function/pom.xml index bd9c0ecb8..8d2cc8def 100644 --- a/examples/spring-function/pom.xml +++ b/examples/spring-function/pom.xml @@ -5,7 +5,7 @@ cloudevents-examples io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 diff --git a/examples/spring-reactive/pom.xml b/examples/spring-reactive/pom.xml index 06313d9ea..5dd82d397 100644 --- a/examples/spring-reactive/pom.xml +++ b/examples/spring-reactive/pom.xml @@ -5,7 +5,7 @@ cloudevents-examples io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 diff --git a/examples/spring-rsocket/pom.xml b/examples/spring-rsocket/pom.xml index 348b4ab64..b7f163772 100644 --- a/examples/spring-rsocket/pom.xml +++ b/examples/spring-rsocket/pom.xml @@ -5,7 +5,7 @@ cloudevents-examples io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 diff --git a/examples/vertx/pom.xml b/examples/vertx/pom.xml index 31d56c4a5..dc88fdd8f 100644 --- a/examples/vertx/pom.xml +++ b/examples/vertx/pom.xml @@ -5,7 +5,7 @@ cloudevents-examples io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 diff --git a/formats/json-jackson/pom.xml b/formats/json-jackson/pom.xml index a1a8146e1..70c88dedd 100644 --- a/formats/json-jackson/pom.xml +++ b/formats/json-jackson/pom.xml @@ -22,7 +22,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../../pom.xml diff --git a/formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormat.java b/formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormat.java index 8454a5a22..542405894 100644 --- a/formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormat.java +++ b/formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormat.java @@ -47,7 +47,7 @@ public final class JsonFormat implements EventFormat { /** * JSON Data Content Type Discriminator */ - private static final Pattern JSON_CONTENT_TYPE_PATTERN = Pattern.compile("^(application|text)\\/([a-zA-Z]+\\+)?json$"); + private static final Pattern JSON_CONTENT_TYPE_PATTERN = Pattern.compile("^(application|text)\\/([a-zA-Z]+\\+)?json(;.*)*$"); private final ObjectMapper mapper; private final JsonFormatOptions options; diff --git a/formats/json-jackson/src/test/java/io/cloudevents/jackson/JsonFormatTest.java b/formats/json-jackson/src/test/java/io/cloudevents/jackson/JsonFormatTest.java index 7e40fe98e..253e2fc89 100644 --- a/formats/json-jackson/src/test/java/io/cloudevents/jackson/JsonFormatTest.java +++ b/formats/json-jackson/src/test/java/io/cloudevents/jackson/JsonFormatTest.java @@ -47,6 +47,22 @@ class JsonFormatTest { private final ObjectMapper mapper = new ObjectMapper(); + @ParameterizedTest + @MethodSource("jsonContentTypes") + void isJsonContentType(String contentType) { + boolean json = JsonFormat.dataIsJsonContentType(contentType); + + assertThat(json).isTrue(); + } + + @ParameterizedTest + @MethodSource("wrongJsonContentTypes") + void isNotJsonContentType(String contentType) { + boolean json = JsonFormat.dataIsJsonContentType(contentType); + + assertThat(json).isFalse(); + } + @ParameterizedTest @MethodSource("serializeTestArgumentsDefault") void serialize(CloudEvent input, String outputFile) throws IOException { @@ -151,6 +167,39 @@ void verifyDeserializeError(String inputFile){ } + static Stream jsonContentTypes() { + return Stream.of( + Arguments.of("application/json"), + Arguments.of("application/json;charset=utf-8"), + Arguments.of("application/json;\tcharset = \"utf-8\""), + Arguments.of("application/cloudevents+json;charset=UTF-8"), + Arguments.of("text/json"), + Arguments.of("text/json;charset=utf-8"), + Arguments.of("text/cloudevents+json;charset=UTF-8"), + Arguments.of("text/json;\twhatever"), + Arguments.of("text/json; boundary=something"), + Arguments.of("text/json;foo=\"bar\""), + Arguments.of("text/json; charset = \"us-ascii\""), + Arguments.of("text/json; \t"), + Arguments.of("text/json;"), + //https://www.rfc-editor.org/rfc/rfc2045#section-5.1 + // any us-ascii char can be part of parameters (except CTRLs and tspecials) + Arguments.of("text/json; char-set = $!#$%&'*+.^_`|"), + Arguments.of((Object) null) + ); + } + + static Stream wrongJsonContentTypes() { + return Stream.of( + Arguments.of("applications/json"), + Arguments.of("application/jsom"), + Arguments.of("application/jsonwrong"), + Arguments.of("text/json "), + Arguments.of("text/json ;"), + Arguments.of("test/json") + ); + } + public static Stream serializeTestArgumentsDefault() { return Stream.of( Arguments.of(V03_MIN, "v03/min.json"), diff --git a/formats/protobuf/pom.xml b/formats/protobuf/pom.xml index 0a8a2f7f8..e1120b594 100644 --- a/formats/protobuf/pom.xml +++ b/formats/protobuf/pom.xml @@ -23,7 +23,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../../pom.xml diff --git a/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoDataWrapper.java b/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoDataWrapper.java index f4b87f5ea..3316787fc 100644 --- a/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoDataWrapper.java +++ b/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoDataWrapper.java @@ -16,8 +16,11 @@ */ package io.cloudevents.protobuf; +import com.google.protobuf.Any; import com.google.protobuf.Message; +import java.util.Arrays; + class ProtoDataWrapper implements ProtoCloudEventData { private final Message protoMessage; @@ -35,4 +38,49 @@ public Message getMessage() { public byte[] toBytes() { return protoMessage.toByteArray(); } + + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return (true); + } + + if (!(obj instanceof ProtoDataWrapper)) { + return (false); + } + + // Now compare the actual data + ProtoDataWrapper rhs = (ProtoDataWrapper) obj; + + if (this.getMessage() == rhs.getMessage()){ + return true; + } + + // This is split out for readability. + // Compare the content in terms onf an 'Any'. + // - Verify the types match + // - Verify the values match. + + final Any lhsAny = getAsAny(this.getMessage()); + final Any rhsAny = getAsAny(rhs.getMessage()); + + final boolean typesMatch = (ProtoSupport.extractMessageType(lhsAny).equals(ProtoSupport.extractMessageType(rhsAny))); + + if (typesMatch) { + return lhsAny.getValue().equals(rhsAny.getValue()); + } else { + return false; + } + } + + private Any getAsAny(Message m) { + + if (m instanceof Any) { + return (Any) m; + } + + return Any.pack(m); + + } } diff --git a/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoDeserializer.java b/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoDeserializer.java index dd55973d5..dc1f54c27 100644 --- a/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoDeserializer.java +++ b/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoDeserializer.java @@ -105,7 +105,7 @@ public , R> R read( data = BytesCloudEventData.wrap(raw); break; case PROTO_DATA: - data = new ProtoAccessor(this.protoCe); + data = new ProtoDataWrapper(this.protoCe.getProtoData()); break; case DATA_NOT_SET: break; @@ -130,22 +130,4 @@ private OffsetDateTime covertProtoTimestamp(com.google.protobuf.Timestamp timest return instant.atOffset(ZoneOffset.UTC); } - private static class ProtoAccessor implements ProtoCloudEventData { - - private final Message message; - - ProtoAccessor(CloudEvent proto){ - this.message = proto.getProtoData(); - } - - @Override - public Message getMessage() { - return message; - } - - @Override - public byte[] toBytes() { - return message.toByteArray(); - } - } } diff --git a/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoSerializer.java b/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoSerializer.java index bc240d84d..2f76d81b8 100644 --- a/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoSerializer.java +++ b/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoSerializer.java @@ -16,11 +16,8 @@ */ package io.cloudevents.protobuf; -import com.google.protobuf.Any; -import com.google.protobuf.ByteString; +import com.google.protobuf.*; import com.google.protobuf.Descriptors.FieldDescriptor; -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.Timestamp; import io.cloudevents.CloudEventData; import io.cloudevents.SpecVersion; import io.cloudevents.core.CloudEventUtils; @@ -248,8 +245,14 @@ public CloudEvent end(CloudEventData data) throws CloudEventRWException { // If it's a proto message we can handle that directly. if (data instanceof ProtoCloudEventData) { final ProtoCloudEventData protoData = (ProtoCloudEventData) data; - if (protoData.getMessage() != null) { - protoBuilder.setProtoData(Any.pack(protoData.getMessage())); + final Message m = protoData.getMessage(); + if (m != null) { + // If it's already an 'Any' don't re-pack it. + if (m instanceof Any) { + protoBuilder.setProtoData((Any) m); + }else { + protoBuilder.setProtoData(Any.pack(m)); + } } } else { if (Objects.equals(dataContentType, PROTO_DATA_CONTENT_TYPE)) { diff --git a/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoSupport.java b/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoSupport.java index a2c079359..a4cf0ec1d 100644 --- a/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoSupport.java +++ b/formats/protobuf/src/main/java/io/cloudevents/protobuf/ProtoSupport.java @@ -17,6 +17,8 @@ package io.cloudevents.protobuf; +import com.google.protobuf.Any; + /** * General support functions. */ @@ -44,4 +46,16 @@ static boolean isTextContent(String contentType) { || contentType.endsWith("+xml") ; } + + /** + * Extract the Protobuf message type from an 'Any' + * @param anyMessage + * @return + */ + static String extractMessageType(final Any anyMessage) { + final String typeUrl = anyMessage.getTypeUrl(); + final String[] parts = typeUrl.split("/"); + + return parts[parts.length -1]; + } } diff --git a/formats/protobuf/src/test/java/io/cloudevents/protobuf/ProtoDataWrapperTest.java b/formats/protobuf/src/test/java/io/cloudevents/protobuf/ProtoDataWrapperTest.java new file mode 100644 index 000000000..232af3209 --- /dev/null +++ b/formats/protobuf/src/test/java/io/cloudevents/protobuf/ProtoDataWrapperTest.java @@ -0,0 +1,98 @@ +/* + * 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. + * 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.protobuf; + +import com.google.protobuf.Any; +import com.google.protobuf.Message; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +class ProtoDataWrapperTest { + + // == Closing Quotes for 2023/02/23 + private final Message quote1 = io.cloudevents.test.v1.proto.Test.Quote.newBuilder() + .setPrice(io.cloudevents.test.v1.proto.Test.Decimal.newBuilder().setScale(2).setUnscaled(7519).build()) + .setHigh(io.cloudevents.test.v1.proto.Test.Decimal.newBuilder().setScale(2).setUnscaled(7628).build()) + .setSymbol("PYPL") + .build(); + + private final Message quote2 = io.cloudevents.test.v1.proto.Test.Quote.newBuilder() + .setPrice(io.cloudevents.test.v1.proto.Test.Decimal.newBuilder().setScale(2).setUnscaled(13097).build()) + .setHigh(io.cloudevents.test.v1.proto.Test.Decimal.newBuilder().setScale(2).setUnscaled(13170).build()) + .setSymbol("IBM") + .build(); + + @Test + public void testBasic() { + + ProtoDataWrapper pdw = new ProtoDataWrapper(quote1); + + assertThat(pdw).isNotNull(); + assertThat(pdw.getMessage()).isNotNull(); + assertThat(pdw.toBytes()).withFailMessage("toBytes was NULL").isNotNull(); + assertThat(pdw.toBytes()).withFailMessage("toBytes[] returned empty array").hasSizeGreaterThan(0); + + // This is current behavior and will probably change in the next version. + assertThat(pdw.getMessage()).isInstanceOf(io.cloudevents.test.v1.proto.Test.Quote.class); + } + + @Test + public void testEquality() { + + ProtoDataWrapper pdw1 = new ProtoDataWrapper(quote1); + ProtoDataWrapper pdw2 = new ProtoDataWrapper(quote1); + + ProtoDataWrapper pdw3 = new ProtoDataWrapper(quote2); + + assertThat(pdw1).withFailMessage("Self Equality Failed - 1").isEqualTo(pdw1); + assertThat(pdw2).withFailMessage("Self Equality Failed - 2").isEqualTo(pdw2); + assertThat(pdw1).withFailMessage("Self Equality Failed - 3").isEqualTo(pdw2); + assertThat(pdw2).withFailMessage("Self Equality Failed - 4").isEqualTo(pdw1); + + assertThat(pdw1).withFailMessage("Non-Equality Failed - 1").isNotEqualTo(null); + assertThat(pdw1).withFailMessage("Non-Equality Failed - 2").isNotEqualTo(pdw3); + assertThat(pdw3).withFailMessage("Non-Equality Failed - 3").isNotEqualTo(pdw2); + + } + + /** + * Verify the generated bytes[] is correct + */ + @Test + public void testBytes() { + + // Our expected 'Any' + final Any expAny = Any.pack(quote1); + + // Our expected 'data' + final byte[] expData = expAny.toByteArray(); + + // Build the wrapper + final ProtoDataWrapper pdw = new ProtoDataWrapper(quote1); + + // Get the actual data + final byte[] actData = pdw.toBytes(); + + // Verify + Arrays.equals(expData, actData); + + } + +} diff --git a/formats/protobuf/src/test/java/io/cloudevents/protobuf/ProtobufFormatTest.java b/formats/protobuf/src/test/java/io/cloudevents/protobuf/ProtobufFormatTest.java index 539811e51..c611ce2bb 100644 --- a/formats/protobuf/src/test/java/io/cloudevents/protobuf/ProtobufFormatTest.java +++ b/formats/protobuf/src/test/java/io/cloudevents/protobuf/ProtobufFormatTest.java @@ -22,19 +22,20 @@ import io.cloudevents.core.format.EventFormat; import io.cloudevents.core.provider.EventFormatProvider; import io.cloudevents.v1.proto.CloudEvent; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.net.URI; +import java.net.URL; import java.time.OffsetDateTime; import java.time.ZoneId; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.net.URL; +import java.util.UUID; import java.util.stream.Stream; import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat; @@ -45,6 +46,46 @@ class ProtobufFormatTest { EventFormat format = new ProtobufFormat(); + public static Stream serializeTestArgumentsDefault() { + return Stream.of( + Arguments.of(V1_MIN, "v1/min.proto.json"), + Arguments.of(V1_WITH_JSON_DATA, "v1/json_data.proto.json"), + Arguments.of(V1_WITH_TEXT_DATA, "v1/text_data.proto.json"), + Arguments.of(V1_WITH_JSON_DATA_WITH_EXT, "v1/json_data_with_ext.proto.json"), + Arguments.of(V1_WITH_XML_DATA, "v1/xml_data.proto.json"), + Arguments.of(V1_WITH_BINARY_EXT, "v1/binary_ext.proto.json"), + + Arguments.of(V03_MIN, "v03/min.proto.json") + + ); + } + + public static Stream roundTripTestArguments() { + return Stream.of( + "v1/min.proto.json", + "v1/json_data.proto.json", + "v1/text_data.proto.json", + "v1/json_data_with_ext.proto.json", + "v1/xml_data.proto.json", + "v1/binary_ext.proto.json", + + "v03/min.proto.json" + ); + } + + private static Message loadProto(String filename) throws IOException { + CloudEvent.Builder b = CloudEvent.newBuilder(); + JsonFormat.parser().ignoringUnknownFields().merge(getReader(filename), b); + return b.build(); + } + + private static Reader getReader(String filename) throws IOException { + URL file = Thread.currentThread().getContextClassLoader().getResource(filename); + assertThat(file).isNotNull(); + File dataFile = new File(file.getFile()); + return new FileReader(dataFile); + } + @Test public void testRegistration() { EventFormat act = EventFormatProvider.getInstance().resolveFormat("application/cloudevents+protobuf"); @@ -90,6 +131,8 @@ public void serialize(io.cloudevents.CloudEvent input, String jsonFile) throws I } + // ---------------------------------------------------------------- + /** * RoundTrip Test *

@@ -131,46 +174,40 @@ public void roundTripTest(String filename) throws IOException { } - public static Stream serializeTestArgumentsDefault() { - return Stream.of( - Arguments.of(V1_MIN, "v1/min.proto.json"), - Arguments.of(V1_WITH_JSON_DATA, "v1/json_data.proto.json"), - Arguments.of(V1_WITH_TEXT_DATA, "v1/text_data.proto.json"), - Arguments.of(V1_WITH_JSON_DATA_WITH_EXT, "v1/json_data_with_ext.proto.json"), - Arguments.of(V1_WITH_XML_DATA, "v1/xml_data.proto.json"), - Arguments.of(V1_WITH_BINARY_EXT, "v1/binary_ext.proto.json"), - - Arguments.of(V03_MIN, "v03/min.proto.json") - - ); - } - - public static Stream roundTripTestArguments() { - return Stream.of( - "v1/min.proto.json", - "v1/json_data.proto.json", - "v1/text_data.proto.json", - "v1/json_data_with_ext.proto.json", - "v1/xml_data.proto.json", - "v1/binary_ext.proto.json", + /** + * This test verifies the fix for Issue #523 that reported the Data + * portion was being corrupted during multi-step round trips if the + * data was a Protobuf Message. + */ + @Test + public void verifyMultiRoundTrip() { + + io.cloudevents.CloudEvent event = CloudEventBuilder.v1() + .withId(UUID.randomUUID().toString()) + .withSource(SOURCE) + .withType(TYPE) + .withData(ProtoCloudEventData.wrap(io.cloudevents.test.v1.proto.Test.Quote.newBuilder() + .setHigh(io.cloudevents.test.v1.proto.Test.Decimal.newBuilder().setScale(2).setUnscaled(4200).build()) + .setPrice(io.cloudevents.test.v1.proto.Test.Decimal.newBuilder().setScale(2).setUnscaled(1000).build()) + .setSymbol("PYPL") + .build())) + .build(); - "v03/min.proto.json" - ); - } + // 1st Round Trip + byte[] raw1 = format.serialize(event); + io.cloudevents.CloudEvent act1 = format.deserialize(raw1); + assertThat(event).withFailMessage("Mismatch on 1st round trip").isEqualTo(act1); - // ---------------------------------------------------------------- + // 2nd Round Trip + byte[] raw2 = format.serialize(act1); + io.cloudevents.CloudEvent act2 = format.deserialize(raw2); + assertThat(event).withFailMessage("Mismatch on 2nd round trip").isEqualTo(act2); - private static Message loadProto(String filename) throws IOException { - CloudEvent.Builder b = CloudEvent.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(getReader(filename), b); - return b.build(); - } + // 3rd Time's a charm + byte[] raw3 = format.serialize(act2); + io.cloudevents.CloudEvent act3 = format.deserialize(raw3); + assertThat(event).withFailMessage("Mismatch on 3rd round trip").isEqualTo(act3); - private static Reader getReader(String filename) throws IOException { - URL file = Thread.currentThread().getContextClassLoader().getResource(filename); - assertThat(file).isNotNull(); - File dataFile = new File(file.getFile()); - return new FileReader(dataFile); } private byte[] getProtoData(String filename) throws IOException { diff --git a/http/basic/pom.xml b/http/basic/pom.xml index 63cd764a9..88bf10bae 100644 --- a/http/basic/pom.xml +++ b/http/basic/pom.xml @@ -21,7 +21,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../../pom.xml diff --git a/http/restful-ws-integration-tests/pom.xml b/http/restful-ws-integration-tests/pom.xml index 8018ba56e..4834619fa 100644 --- a/http/restful-ws-integration-tests/pom.xml +++ b/http/restful-ws-integration-tests/pom.xml @@ -22,7 +22,7 @@ cloudevents-parent io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/http/restful-ws-integration-tests/restful-ws-common/pom.xml b/http/restful-ws-integration-tests/restful-ws-common/pom.xml index 63a17aae0..c01023c73 100644 --- a/http/restful-ws-integration-tests/restful-ws-common/pom.xml +++ b/http/restful-ws-integration-tests/restful-ws-common/pom.xml @@ -22,7 +22,7 @@ cloudevents-http-restful-ws-integration-tests io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../ 4.0.0 diff --git a/http/restful-ws-integration-tests/restful-ws-jersey/pom.xml b/http/restful-ws-integration-tests/restful-ws-jersey/pom.xml index cb5f2b84e..84bf3a535 100644 --- a/http/restful-ws-integration-tests/restful-ws-jersey/pom.xml +++ b/http/restful-ws-integration-tests/restful-ws-jersey/pom.xml @@ -22,7 +22,7 @@ cloudevents-http-restful-ws-integration-tests io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../ 4.0.0 diff --git a/http/restful-ws-integration-tests/restful-ws-resteasy/pom.xml b/http/restful-ws-integration-tests/restful-ws-resteasy/pom.xml index f778f7fa2..b7c1172af 100644 --- a/http/restful-ws-integration-tests/restful-ws-resteasy/pom.xml +++ b/http/restful-ws-integration-tests/restful-ws-resteasy/pom.xml @@ -22,7 +22,7 @@ cloudevents-http-restful-ws-integration-tests io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../ 4.0.0 diff --git a/http/restful-ws-integration-tests/restful-ws-spring/pom.xml b/http/restful-ws-integration-tests/restful-ws-spring/pom.xml index c9e329775..f7e6c3c2c 100644 --- a/http/restful-ws-integration-tests/restful-ws-spring/pom.xml +++ b/http/restful-ws-integration-tests/restful-ws-spring/pom.xml @@ -22,7 +22,7 @@ cloudevents-http-restful-ws-integration-tests io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../ 4.0.0 diff --git a/http/restful-ws-jakarta-integration-tests/pom.xml b/http/restful-ws-jakarta-integration-tests/pom.xml new file mode 100644 index 000000000..967e418e5 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/pom.xml @@ -0,0 +1,30 @@ + + + + cloudevents-parent + io.cloudevents + 2.5.1-SNAPSHOT + ../../pom.xml + + 4.0.0 + + cloudevents-http-restful-ws-jakarta-integration-tests + CloudEvents - JAX-RS Jakarta EE9+ Web Http Binding Integration Tests + pom + + + + true + + + + restful-ws-jakarta-common + restful-ws-resteasy + + + restful-ws-liberty + + + diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-jakarta-common/pom.xml b/http/restful-ws-jakarta-integration-tests/restful-ws-jakarta-common/pom.xml new file mode 100644 index 000000000..5cc348940 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-jakarta-common/pom.xml @@ -0,0 +1,38 @@ + + + cloudevents-http-restful-ws-jakarta-integration-tests + io.cloudevents + 2.5.1-SNAPSHOT + ../ + + 4.0.0 + cloudevents-http-restful-ws-jakarta-integration-tests-common + CloudEvents - JAX-RS Jakarta EE9+ Integration Tests - Common + jar + + + + io.cloudevents + cloudevents-http-restful-ws-jakarta + ${project.version} + + + io.cloudevents + cloudevents-core + tests + test-jar + ${project.version} + + + org.assertj + assertj-core + ${assertj-core.version} + + + org.junit.jupiter + junit-jupiter + ${junit-jupiter.version} + + + diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-jakarta-common/src/main/java/io/cloudevents/http/restful/ws/BaseTest.java b/http/restful-ws-jakarta-integration-tests/restful-ws-jakarta-common/src/main/java/io/cloudevents/http/restful/ws/BaseTest.java new file mode 100644 index 000000000..ea5cc7569 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-jakarta-common/src/main/java/io/cloudevents/http/restful/ws/BaseTest.java @@ -0,0 +1,103 @@ +/* + * 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. + * 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.http.restful.ws; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.mock.CSVFormat; +import io.cloudevents.core.test.Data; +import org.junit.jupiter.api.Test; + +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; + +import static org.assertj.core.api.Assertions.assertThat; + +public abstract class BaseTest { + + protected abstract WebTarget getWebTarget(); + + @Test + void getMinEvent() { + Response res = getWebTarget().path("getMinEvent").request().buildGet().invoke(); + + assertThat(res.getHeaderString("ce-specversion")) + .isEqualTo("1.0"); + + CloudEvent outEvent = res.readEntity(CloudEvent.class); + assertThat(outEvent) + .isEqualTo(Data.V1_MIN); + } + + @Test + void getStructuredEvent() { + Response res = getWebTarget().path("getStructuredEvent").request().buildGet().invoke(); + + CloudEvent outEvent = res.readEntity(CloudEvent.class); + assertThat(outEvent) + .isEqualTo(Data.V1_MIN); + assertThat(res.getHeaderString(HttpHeaders.CONTENT_TYPE)) + .isEqualTo(CSVFormat.INSTANCE.serializedContentType()); + } + + @Test + void getEvent() { + Response res = getWebTarget().path("getEvent").request().buildGet().invoke(); + + CloudEvent outEvent = res.readEntity(CloudEvent.class); + assertThat(outEvent) + .isEqualTo(Data.V1_WITH_JSON_DATA_WITH_EXT_STRING); + } + + @Test + void postEventWithoutBody() { + Response res = getWebTarget() + .path("postEventWithoutBody") + .request() + .buildPost(Entity.entity(Data.V1_MIN, CloudEventsProvider.CLOUDEVENT_TYPE)) + .invoke(); + + assertThat(res.getStatus()) + .isEqualTo(200); + } + + @Test + void postEventStructured() { + Response res = getWebTarget() + .path("postEventWithoutBody") + .request() + .buildPost(Entity.entity(Data.V1_MIN, "application/cloudevents+csv")) + .invoke(); + + assertThat(res.getStatus()) + .isEqualTo(200); + } + + @Test + void postEvent() { + Response res = getWebTarget() + .path("postEvent") + .request() + .buildPost(Entity.entity(Data.V1_WITH_JSON_DATA_WITH_EXT_STRING, CloudEventsProvider.CLOUDEVENT_TYPE)) + .invoke(); + + assertThat(res.getStatus()) + .isEqualTo(200); + } +} diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-jakarta-common/src/main/java/io/cloudevents/http/restful/ws/TestResource.java b/http/restful-ws-jakarta-integration-tests/restful-ws-jakarta-common/src/main/java/io/cloudevents/http/restful/ws/TestResource.java new file mode 100644 index 000000000..923a8572d --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-jakarta-common/src/main/java/io/cloudevents/http/restful/ws/TestResource.java @@ -0,0 +1,67 @@ +/* + * 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. + * 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.http.restful.ws; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.test.Data; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; + +@Path("/") +public class TestResource { + + @GET + @Path("getMinEvent") + public CloudEvent getMinEvent() { + return Data.V1_MIN; + } + + @GET + @Path("getStructuredEvent") + @StructuredEncoding("application/cloudevents+csv") + public CloudEvent getStructuredEvent() { + return Data.V1_MIN; + } + + @GET + @Path("getEvent") + public CloudEvent getEvent() { + return Data.V1_WITH_JSON_DATA_WITH_EXT_STRING; + } + + @POST + @Path("postEventWithoutBody") + public Response postEventWithoutBody(CloudEvent inputEvent) { + if (inputEvent.equals(Data.V1_MIN)) { + return Response.ok().build(); + } + return Response.serverError().build(); + } + + @POST + @Path("postEvent") + public Response postEvent(CloudEvent inputEvent) { + if (inputEvent.equals(Data.V1_WITH_JSON_DATA_WITH_EXT_STRING)) { + return Response.ok().build(); + } + return Response.serverError().build(); + } +} diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-jersey/pom.xml b/http/restful-ws-jakarta-integration-tests/restful-ws-jersey/pom.xml new file mode 100644 index 000000000..fdf8bd9f5 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-jersey/pom.xml @@ -0,0 +1,73 @@ + + + + + + cloudevents-http-restful-ws-jakarta-integration-tests + io.cloudevents + 2.5.0-SNAPSHOT + ../ + + 4.0.0 + + cloudevents-http-restful-ws-jakarta-integration-tests-jersey + CloudEvents - JAX-RS Jakarta EE9+ Integration Tests - Jersey + jar + + + 3.0.8 + 3.0.0 + + + + + + io.cloudevents + cloudevents-http-restful-ws-jakarta-integration-tests-common + ${project.version} + test + + + jakarta.ws.rs + jakarta.ws.rs-api + ${jakarta-ee.version} + test + + + org.glassfish.jersey.test-framework.providers + jersey-test-framework-provider-jetty + ${jersey.version} + test + + + org.glassfish.jersey.inject + jersey-hk2 + ${jersey.version} + test + + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter.version} + test + + + + diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-jersey/src/test/java/com/github/hanleyt/JerseyExtension.java b/http/restful-ws-jakarta-integration-tests/restful-ws-jersey/src/test/java/com/github/hanleyt/JerseyExtension.java new file mode 100644 index 000000000..066535c05 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-jersey/src/test/java/com/github/hanleyt/JerseyExtension.java @@ -0,0 +1,130 @@ +/* + * Ported from https://github.com/hanleyt/jersey-junit as no version supports Jesery versions >=3.0.0 + * + * Only update is the replacement of the ws-rs package namespace from javax. to jakarta. + */ + +package com.github.hanleyt; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.test.DeploymentContext; +import org.glassfish.jersey.test.JerseyTest; +import org.glassfish.jersey.test.TestProperties; +import org.glassfish.jersey.test.spi.TestContainerException; +import org.glassfish.jersey.test.spi.TestContainerFactory; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Application; +import java.net.URI; +import java.util.Arrays; +import java.util.Collection; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +public class JerseyExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver { + + private static final Collection> INJECTABLE_PARAMETER_TYPES = Arrays.asList(Client.class, WebTarget.class, URI.class); + + private final Function testContainerFactoryProvider; + private final Function deploymentContextProvider; + private final BiFunction configProvider; + + private JerseyExtension() { + throw new IllegalStateException("JerseyExtension must be registered programmatically"); + } + + public JerseyExtension(Supplier applicationSupplier) { + this((unused) -> applicationSupplier.get(), null); + } + + public JerseyExtension(Supplier applicationSupplier, + BiFunction configProvider) { + this((unused) -> applicationSupplier.get(), configProvider); + } + + public JerseyExtension(Function applicationProvider) { + this(applicationProvider, null); + } + + public JerseyExtension(Function applicationProvider, + BiFunction configProvider) { + this(null, (context) -> DeploymentContext.builder(applicationProvider.apply(context)).build(), configProvider); + } + + public JerseyExtension(Function testContainerFactoryProvider, + Function deploymentContextProvider, + BiFunction configProvider) { + this.testContainerFactoryProvider = testContainerFactoryProvider; + this.deploymentContextProvider = deploymentContextProvider; + this.configProvider = configProvider; + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + JerseyTest jerseyTest = initJerseyTest(context); + getStore(context).put(Client.class, jerseyTest.client()); + getStore(context).put(WebTarget.class, jerseyTest.target()); + getStore(context).put(URI.class, jerseyTest.target().getUri()); + } + + private JerseyTest initJerseyTest(ExtensionContext context) throws Exception { + JerseyTest jerseyTest = new JerseyTest() { + + @Override + protected DeploymentContext configureDeployment() { + forceSet(TestProperties.CONTAINER_PORT, "0"); + return deploymentContextProvider.apply(context); + } + + @Override + protected TestContainerFactory getTestContainerFactory() throws TestContainerException { + if (testContainerFactoryProvider != null) { + return testContainerFactoryProvider.apply(context); + } + return super.getTestContainerFactory(); + } + + @Override + protected void configureClient(ClientConfig config) { + if (configProvider != null) { + config = configProvider.apply(context, config); + } + super.configureClient(config); + } + }; + jerseyTest.setUp(); + getStore(context).put(JerseyTest.class, jerseyTest); + return jerseyTest; + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + ExtensionContext.Store store = getStore(context); + store.remove(JerseyTest.class, JerseyTest.class).tearDown(); + INJECTABLE_PARAMETER_TYPES.forEach(store::remove); + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + Class parameterType = parameterContext.getParameter().getType(); + return INJECTABLE_PARAMETER_TYPES.contains(parameterType); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + Class parameterType = parameterContext.getParameter().getType(); + return getStore(extensionContext).get(parameterType, parameterType); + } + + public static ExtensionContext.Store getStore(ExtensionContext context) { + return context.getStore(ExtensionContext.Namespace.GLOBAL); + } + +} diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-jersey/src/test/java/io/cloudevents/http/restful/ws/jakarta/jersey/TestJersey.java b/http/restful-ws-jakarta-integration-tests/restful-ws-jersey/src/test/java/io/cloudevents/http/restful/ws/jakarta/jersey/TestJersey.java new file mode 100644 index 000000000..fb54c3164 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-jersey/src/test/java/io/cloudevents/http/restful/ws/jakarta/jersey/TestJersey.java @@ -0,0 +1,68 @@ +/* + * 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. + * 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.http.restful.ws.jakarta.jersey; + +import com.github.hanleyt.JerseyExtension; +import io.cloudevents.core.mock.CSVFormat; +import io.cloudevents.core.provider.EventFormatProvider; +import io.cloudevents.http.restful.ws.BaseTest; +import io.cloudevents.http.restful.ws.CloudEventsProvider; +import io.cloudevents.http.restful.ws.TestResource; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; + +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Application; + +public class TestJersey extends BaseTest { + + private WebTarget target; + + @Override + protected WebTarget getWebTarget() { + return target; + } + + @BeforeAll + public static void beforeAll() { + EventFormatProvider.getInstance().registerFormat(CSVFormat.INSTANCE); + } + + @BeforeEach + void beforeEach(WebTarget target) { + this.target = target; + } + + @RegisterExtension + JerseyExtension jerseyExtension = new JerseyExtension(this::configureJersey, this::configureJerseyClient); + + private Application configureJersey() { + return new ResourceConfig(TestResource.class) + .register(CloudEventsProvider.class); + } + + private ClientConfig configureJerseyClient(ExtensionContext extensionContext, ClientConfig clientConfig) { + return clientConfig + .register(CloudEventsProvider.class); + } + +} diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/pom.xml b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/pom.xml new file mode 100644 index 000000000..15fc7940f --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/pom.xml @@ -0,0 +1,161 @@ + + + + cloudevents-http-restful-ws-jakarta-integration-tests + io.cloudevents + 2.5.1-SNAPSHOT + ../ + + 4.0.0 + + cloudevents-http-restful-ws-jakarta-integration-tests-microprofile + CloudEvents - JAX-RS Jakarta EE9+ Integration Tests - Microprofile and Liberty + war + + + + 9080 + 9443 + + microprofile-test + + + + + + org.jboss.arquillian + arquillian-bom + 1.6.0.Final + pom + import + + + + + + + + jakarta.platform + jakarta.jakartaee-api + 9.1.0 + provided + + + org.eclipse.microprofile + microprofile + 5.0 + pom + provided + + + io.cloudevents + cloudevents-http-restful-ws-jakarta + ${project.version} + + + io.cloudevents + cloudevents-core + tests + test-jar + ${project.version} + + + org.jboss.resteasy + resteasy-client + 6.0.3.Final + test + + + org.jboss.resteasy + resteasy-json-binding-provider + 6.0.3.Final + test + + + org.glassfish + jakarta.json + 2.0.1 + test + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + javax.activation + activation + 1.1.1 + + + io.openliberty.arquillian + arquillian-liberty-managed-jakarta-junit + 2.1.0 + pom + test + + + org.jboss.shrinkwrap + shrinkwrap-api + test + + + + + ${project.artifactId} + + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + + pom.xml + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.2 + + + ${arquillian.war.name}.war + + + + + test + + integration-test + + + + + + io.openliberty.tools + liberty-maven-plugin + 3.5.1 + + + + -Dsystem.context.root=/${arquillian.war.name} + + + + + + arquillian-configuration + generate-test-resources + + create + install-feature + configure-arquillian + + + + + + + diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/main/java/io/cloudevents/restful/mp/test/MpCEApp.java b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/main/java/io/cloudevents/restful/mp/test/MpCEApp.java new file mode 100644 index 000000000..279cdb3e4 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/main/java/io/cloudevents/restful/mp/test/MpCEApp.java @@ -0,0 +1,9 @@ +package io.cloudevents.restful.mp.test; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +@ApplicationPath("/") +public class MpCEApp extends Application { + +} diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/main/java/io/cloudevents/restful/mp/test/TestResource.java b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/main/java/io/cloudevents/restful/mp/test/TestResource.java new file mode 100644 index 000000000..5a73efeb6 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/main/java/io/cloudevents/restful/mp/test/TestResource.java @@ -0,0 +1,60 @@ +package io.cloudevents.restful.mp.test; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.mock.CSVFormat; +import io.cloudevents.core.provider.EventFormatProvider; +import io.cloudevents.core.test.Data; +import io.cloudevents.http.restful.ws.StructuredEncoding; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.RequestScoped; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; + +@RequestScoped +@Path("/") +public class TestResource{ + + @PostConstruct + void init() { + EventFormatProvider.getInstance().registerFormat(CSVFormat.INSTANCE); + } + + @GET + @Path("getMinEvent") + public CloudEvent getMinEvent() { + return Data.V1_MIN; + } + + @GET + @Path("getStructuredEvent") + @StructuredEncoding("application/cloudevents+csv") + public CloudEvent getStructuredEvent() { + return Data.V1_MIN; + } + + @GET + @Path("getEvent") + public CloudEvent getEvent() { + return Data.V1_WITH_JSON_DATA_WITH_EXT_STRING; + } + + @POST + @Path("postEventWithoutBody") + public Response postEventWithoutBody(CloudEvent inputEvent) { + if (inputEvent.equals(Data.V1_MIN)) { + return Response.ok().build(); + } + return Response.serverError().build(); + } + + @POST + @Path("postEvent") + public Response postEvent(CloudEvent inputEvent) { + if (inputEvent.equals(Data.V1_WITH_JSON_DATA_WITH_EXT_STRING)) { + return Response.ok().build(); + } + return Response.serverError().build(); + } +} diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/main/liberty/config/server.xml b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/main/liberty/config/server.xml new file mode 100644 index 000000000..9725cb9c8 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/main/liberty/config/server.xml @@ -0,0 +1,18 @@ + + + + microProfile-5.0 + + localConnector-1.0 + servlet-5.0 + + + + + + + + + + diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/test/java/io/cloudevents/http/restful/ws/jakarta/microprofile/TestMicroprofile.java b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/test/java/io/cloudevents/http/restful/ws/jakarta/microprofile/TestMicroprofile.java new file mode 100644 index 000000000..184fa2c8d --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-liberty/src/test/java/io/cloudevents/http/restful/ws/jakarta/microprofile/TestMicroprofile.java @@ -0,0 +1,123 @@ +package io.cloudevents.http.restful.ws.jakarta.microprofile; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.mock.CSVFormat; +import io.cloudevents.core.test.Data; +import io.cloudevents.http.restful.ws.CloudEventsProvider; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.URL; + + +/** + * Arquilian does not support assertj, so test cases have been ported to Junit to work with arquilian + */ +@RunWith(Arquillian.class) +public class TestMicroprofile { + + private static final String WARNAME = "microprofile-test.war"; + private Client client = ClientBuilder.newClient(); + + @Deployment(testable = true) + public static WebArchive createDeployment() { + System.out.println(WARNAME); + WebArchive archive = ShrinkWrap.create(WebArchive.class, WARNAME).addPackages(true,"io.cloudevents"); + return archive; + } + + @ArquillianResource + private URL baseURL; + + private WebTarget webTarget; + + public WebTarget getWebTarget() { + if(webTarget == null){ + webTarget = client.target(baseURL.toString()); + webTarget.register(CloudEventsProvider.class); + } + return webTarget; + } + + @Test + @RunAsClient + public void getMinEvent() { + Response res = getWebTarget().path("getMinEvent").request().buildGet().invoke(); + + Assert.assertEquals("1.0",res.getHeaderString("ce-specversion")); + Assert.assertEquals(Data.V1_MIN,res.readEntity(CloudEvent.class)); + + res.close(); + } + + @Test + @RunAsClient + public void getStructuredEvent() { + Response res = getWebTarget().path("getStructuredEvent").request().buildGet().invoke(); + + Assert.assertEquals(Data.V1_MIN,res.readEntity(CloudEvent.class)); + Assert.assertEquals(CSVFormat.INSTANCE.serializedContentType(),res.getHeaderString(HttpHeaders.CONTENT_TYPE)); + + res.close(); + } + + @Test + @RunAsClient + public void testGetEvent() throws Exception { + Response response = getWebTarget().path("getEvent").request().buildGet().invoke(); + + Assert.assertEquals("Valid response code", 200, response.getStatus()); + Assert.assertEquals("should match", Data.V1_WITH_JSON_DATA_WITH_EXT_STRING, response.readEntity(CloudEvent.class)); + + response.close(); + } + + @Test + @RunAsClient + public void postEventWithoutBody() { + Response res = getWebTarget() + .path("postEventWithoutBody") + .request() + .buildPost(Entity.entity(Data.V1_MIN, CloudEventsProvider.CLOUDEVENT_TYPE)) + .invoke(); + + Assert.assertEquals(200,res.getStatus()); + } + + @Test + @RunAsClient + public void postEventStructured() { + Response res = getWebTarget() + .path("postEventWithoutBody") + .request() + .buildPost(Entity.entity(Data.V1_MIN, "application/cloudevents+csv")) + .invoke(); + + Assert.assertEquals(200,res.getStatus()); + } + + @Test + @RunAsClient + public void postEvent() { + Response res = getWebTarget() + .path("postEvent") + .request() + .buildPost(Entity.entity(Data.V1_WITH_JSON_DATA_WITH_EXT_STRING, CloudEventsProvider.CLOUDEVENT_TYPE)) + .invoke(); + + Assert.assertEquals(200,res.getStatus()); + } +} diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-resteasy/pom.xml b/http/restful-ws-jakarta-integration-tests/restful-ws-resteasy/pom.xml new file mode 100644 index 000000000..fa118be27 --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-resteasy/pom.xml @@ -0,0 +1,45 @@ + + + cloudevents-http-restful-ws-jakarta-integration-tests + io.cloudevents + 2.5.1-SNAPSHOT + ../ + + 4.0.0 + + cloudevents-http-restful-ws-jakarta-integration-tests-resteasy + CloudEvents - JAX-RS Jakarta EE9+ Integration Tests - RESTEasy + jar + + + + 4.3.3 + 6.0.3.Final + + + + + + io.cloudevents + cloudevents-http-restful-ws-jakarta-integration-tests-common + ${project.version} + test + + + + io.vertx + vertx-core + ${vertx.version} + test + + + org.jboss.resteasy + resteasy-vertx + ${resteasy.version} + test + + + + + diff --git a/http/restful-ws-jakarta-integration-tests/restful-ws-resteasy/src/test/java/io/cloudevents/http/restful/ws/jakarta/resteasy/TestResteasy.java b/http/restful-ws-jakarta-integration-tests/restful-ws-resteasy/src/test/java/io/cloudevents/http/restful/ws/jakarta/resteasy/TestResteasy.java new file mode 100644 index 000000000..5cac8fdff --- /dev/null +++ b/http/restful-ws-jakarta-integration-tests/restful-ws-resteasy/src/test/java/io/cloudevents/http/restful/ws/jakarta/resteasy/TestResteasy.java @@ -0,0 +1,62 @@ + +/* + * 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. + * 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.http.restful.ws.jakarta.resteasy; + +import io.cloudevents.core.mock.CSVFormat; +import io.cloudevents.core.provider.EventFormatProvider; +import io.cloudevents.http.restful.ws.BaseTest; +import io.cloudevents.http.restful.ws.CloudEventsProvider; +import io.cloudevents.http.restful.ws.TestResource; +import org.jboss.resteasy.plugins.server.vertx.VertxContainer; +import org.jboss.resteasy.plugins.server.vertx.VertxResteasyDeployment; +import org.jboss.resteasy.test.TestPortProvider; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.WebTarget; + +public class TestResteasy extends BaseTest { + + private static VertxResteasyDeployment resteasyDeployment; + private static WebTarget target; + + @Override + protected WebTarget getWebTarget() { + return target; + } + + @BeforeAll + public static void beforeClass() throws Exception { + EventFormatProvider.getInstance().registerFormat(CSVFormat.INSTANCE); + + String base = TestPortProvider.generateBaseUrl(); + TestResteasy.resteasyDeployment = VertxContainer.start(base); + TestResteasy.resteasyDeployment.getProviderFactory().register(CloudEventsProvider.class); + TestResteasy.resteasyDeployment.getRegistry().addPerRequestResource(TestResource.class); + + TestResteasy.target = ClientBuilder.newClient().register(CloudEventsProvider.class).target(base); + } + + @AfterAll + public static void after() throws Exception { + TestResteasy.resteasyDeployment.stop(); + } + +} diff --git a/http/restful-ws-jakarta/README.md b/http/restful-ws-jakarta/README.md new file mode 100644 index 000000000..8f676ce2e --- /dev/null +++ b/http/restful-ws-jakarta/README.md @@ -0,0 +1,7 @@ +# HTTP Protocol Binding for Jakarta EE9+ - Jakarta RESTful Web Services + +Javadocs: [![Javadocs](http://www.javadoc.io/badge/io.cloudevents/cloudevents-http-restful-ws.svg?color=green)](http://www.javadoc.io/doc/io.cloudevents/cloudevents-http-restful-ws) + +Documentation: https://cloudevents.github.io/sdk-java/http-jakarta-restful-ws + +The code for this package lies within the [restful-ws](https://github.com/cloudevents/sdk-java/tree/master/http/restful-ws) directory and is copied within here and has `javax.` replaced with `jakarta.` diff --git a/http/restful-ws-jakarta/pom.xml b/http/restful-ws-jakarta/pom.xml new file mode 100644 index 000000000..dcb5203ea --- /dev/null +++ b/http/restful-ws-jakarta/pom.xml @@ -0,0 +1,97 @@ + + + + 4.0.0 + + io.cloudevents + cloudevents-parent + 2.5.1-SNAPSHOT + ../../pom.xml + + + cloudevents-http-restful-ws-jakarta + CloudEvents - Jakarta EE 9+ - Jakarta RESTful Web Services Http Binding + jar + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + default + + true + + + + + + 3.0.0 + 3.0.8 + 4.4.3 + 6.0.3.Final + io.cloudevents.http.restfulws + + + + + io.cloudevents + cloudevents-core + ${project.version} + + + jakarta.ws.rs + jakarta.ws.rs-api + ${jakarta-ee.version} + + + + + + + maven-antrun-plugin + 3.0.0 + + + generate-sources + + + + + + + + + + + + + + + + + + run + + + + + + + + diff --git a/http/restful-ws/pom.xml b/http/restful-ws/pom.xml index ce58fe5dc..f14b5ceaf 100644 --- a/http/restful-ws/pom.xml +++ b/http/restful-ws/pom.xml @@ -21,7 +21,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../../pom.xml diff --git a/http/vertx/pom.xml b/http/vertx/pom.xml index be808981a..f1704aa27 100644 --- a/http/vertx/pom.xml +++ b/http/vertx/pom.xml @@ -22,7 +22,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT ../../pom.xml diff --git a/kafka/pom.xml b/kafka/pom.xml index a7caae6e5..559f7600b 100644 --- a/kafka/pom.xml +++ b/kafka/pom.xml @@ -23,7 +23,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT cloudevents-kafka diff --git a/pom.xml b/pom.xml index f63d8a47a..63c41de59 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT pom CloudEvents @@ -74,6 +74,7 @@ http/basic http/vertx http/restful-ws + http/restful-ws-jakarta kafka spring sql @@ -168,6 +169,7 @@ https://qpid.apache.org/releases/qpid-proton-j-0.33.7/api/ https://fasterxml.github.io/jackson-databind/javadoc/2.10/ + 8 @@ -209,6 +211,7 @@ benchmarks http/restful-ws-integration-tests + http/restful-ws-jakarta-integration-tests examples diff --git a/spring/pom.xml b/spring/pom.xml index 945554a87..348e6a700 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -23,7 +23,7 @@ io.cloudevents cloudevents-parent - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT cloudevents-spring diff --git a/sql/pom.xml b/sql/pom.xml index 746f8a569..801f2e297 100644 --- a/sql/pom.xml +++ b/sql/pom.xml @@ -5,7 +5,7 @@ cloudevents-parent io.cloudevents - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 4.0.0 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy