Add Quarkus example (#226)
* Add Quarkus example Signed-off-by: ruromero <rromerom@redhat.com> * Rename project and remove unrelated files Signed-off-by: ruromero <rromerom@redhat.com>
This commit is contained in:
parent
c3904fbff4
commit
29c9eaa23f
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>kafka</module>
|
<module>kafka</module>
|
||||||
|
<module>restful-ws-quarkus</module>
|
||||||
<module>vertx</module>
|
<module>vertx</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
# Cloudevents Restful WS Quarkus example
|
||||||
|
|
||||||
|
This sample application has a `/users` REST endpoint in which you can manage the different users.
|
||||||
|
The way to create users is through CloudEvents. Here is an example POST:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
curl -v http://localhost:8080/users \
|
||||||
|
-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 @examples/user.json
|
||||||
|
|
||||||
|
> POST /users HTTP/1.1
|
||||||
|
> Host: localhost:8080
|
||||||
|
> User-Agent: curl/7.69.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: 88
|
||||||
|
|
||||||
|
< HTTP/1.1 201 Created
|
||||||
|
< Content-Length: 0
|
||||||
|
< Location: http://localhost:8080/users
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to also show how to create and send CloudEvents, generated events will be periodically sent
|
||||||
|
each 2 seconds through HTTP to the same endpoint using a REST client.
|
||||||
|
|
||||||
|
Check the following URL to view the existing users:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
$ curl http://localhost:8080/users
|
||||||
|
{
|
||||||
|
"user1": {
|
||||||
|
"username": "user1",
|
||||||
|
"firstName": "firstName1",
|
||||||
|
"lastName": "lastName1",
|
||||||
|
"age": 1
|
||||||
|
},
|
||||||
|
"user2": {
|
||||||
|
"username": "user2",
|
||||||
|
"firstName": "firstName2",
|
||||||
|
"lastName": "lastName2",
|
||||||
|
"age": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build and execution
|
||||||
|
|
||||||
|
This project uses Quarkus, the Supersonic Subatomic Java Framework.
|
||||||
|
|
||||||
|
If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ .
|
||||||
|
|
||||||
|
### Running the application in dev mode
|
||||||
|
|
||||||
|
You can run your application in dev mode that enables live coding using:
|
||||||
|
```
|
||||||
|
mvn quarkus:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Packaging and running the application
|
||||||
|
|
||||||
|
The application can be packaged using `mvn package`.
|
||||||
|
It produces the `cloudevents-restful-ws-quarkus-example-1.0-SNAPSHOT-runner.jar` file in the `/target` directory.
|
||||||
|
Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/lib` directory.
|
||||||
|
|
||||||
|
The application is now runnable using `java -jar target/cloudevents-restful-ws-quarkus-example-1.0-SNAPSHOT-runner.jar`.
|
||||||
|
|
||||||
|
### Creating a native executable
|
||||||
|
|
||||||
|
You can create a native executable using: `mvn package -Pnative`.
|
||||||
|
|
||||||
|
Or, if you don't have GraalVM installed, you can run the native executable build in a container using: `mvn package -Pnative -Dquarkus.native.container-build=true`.
|
||||||
|
|
||||||
|
You can then execute your native executable with: `./target/cloudevents-restful-ws-quarkus-example-1.0-SNAPSHOT-runner`
|
||||||
|
|
||||||
|
If you want to learn more about building native executables, please consult https://quarkus.io/guides/building-native-image.
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"username": "jsmith",
|
||||||
|
"firstName": "John",
|
||||||
|
"lastName": "Smith",
|
||||||
|
"age": 37
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<parent>
|
||||||
|
<artifactId>cloudevents-examples</artifactId>
|
||||||
|
<groupId>io.cloudevents</groupId>
|
||||||
|
<version>2.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>cloudevents-restful-ws-quarkus-example</artifactId>
|
||||||
|
<properties>
|
||||||
|
<quarkus-plugin.version>1.7.1.Final</quarkus-plugin.version>
|
||||||
|
<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
|
||||||
|
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
|
||||||
|
<quarkus.platform.version>1.7.1.Final</quarkus.platform.version>
|
||||||
|
</properties>
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${quarkus.platform.group-id}</groupId>
|
||||||
|
<artifactId>${quarkus.platform.artifact-id}</artifactId>
|
||||||
|
<version>${quarkus.platform.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-resteasy-jackson</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.cloudevents</groupId>
|
||||||
|
<artifactId>cloudevents-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.cloudevents</groupId>
|
||||||
|
<artifactId>cloudevents-http-restful-ws</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.cloudevents</groupId>
|
||||||
|
<artifactId>cloudevents-json-jackson</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-junit5</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.rest-assured</groupId>
|
||||||
|
<artifactId>rest-assured</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-maven-plugin</artifactId>
|
||||||
|
<version>${quarkus-plugin.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>prepare</goal>
|
||||||
|
<goal>prepare-tests</goal>
|
||||||
|
<goal>build</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>native</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>native</name>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
|
<version>${maven-surefire-plugin.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>integration-test</goal>
|
||||||
|
<goal>verify</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<native.image.path>
|
||||||
|
${project.build.directory}/${project.build.finalName}-runner
|
||||||
|
</native.image.path>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<properties>
|
||||||
|
<quarkus.package.type>native</quarkus.package.type>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package io.cloudevents.examples.quarkus.client;
|
||||||
|
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import io.cloudevents.CloudEvent;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||||
|
|
||||||
|
@Path("/users")
|
||||||
|
@RegisterRestClient
|
||||||
|
public interface UserClient {
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
void emit(CloudEvent event);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package io.cloudevents.examples.quarkus.client;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.enterprise.context.ApplicationScoped;
|
||||||
|
import javax.enterprise.event.Observes;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import io.cloudevents.CloudEvent;
|
||||||
|
import io.cloudevents.core.builder.CloudEventBuilder;
|
||||||
|
import io.cloudevents.examples.quarkus.model.User;
|
||||||
|
import io.quarkus.runtime.StartupEvent;
|
||||||
|
import io.smallrye.mutiny.Multi;
|
||||||
|
import io.vertx.core.json.Json;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class UserEventsGenerator {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(UserEventsGenerator.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@RestClient
|
||||||
|
UserClient userClient;
|
||||||
|
|
||||||
|
public void init(@Observes StartupEvent startupEvent) {
|
||||||
|
Multi.createFrom().ticks().every(Duration.ofSeconds(2))
|
||||||
|
.onItem()
|
||||||
|
.transform(this::createEvent)
|
||||||
|
.subscribe()
|
||||||
|
.with(event -> {
|
||||||
|
LOGGER.info("try to emit user: {}", event.getId());
|
||||||
|
userClient.emit(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private CloudEvent createEvent(long id) {
|
||||||
|
return CloudEventBuilder.v1()
|
||||||
|
.withSource(URI.create("example"))
|
||||||
|
.withType("io.cloudevents.examples.quarkus.user")
|
||||||
|
.withId(UUID.randomUUID().toString())
|
||||||
|
.withDataContentType(MediaType.APPLICATION_JSON)
|
||||||
|
.withData(createUserAsByteArray(id))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] createUserAsByteArray(Long id) {
|
||||||
|
User user = new User()
|
||||||
|
.setAge(id.intValue())
|
||||||
|
.setUsername("user" + id)
|
||||||
|
.setFirstName("firstName" + id)
|
||||||
|
.setLastName("lastName" + id);
|
||||||
|
return Json.encode(user).getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package io.cloudevents.examples.quarkus.model;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private Integer age;
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getAge() {
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User setAge(Integer age) {
|
||||||
|
this.age = age;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "User{" +
|
||||||
|
"username='" + username + '\'' +
|
||||||
|
", firstName='" + firstName + '\'' +
|
||||||
|
", lastName='" + lastName + '\'' +
|
||||||
|
", age=" + age +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package io.cloudevents.examples.quarkus.resources;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.NotFoundException;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
|
import io.cloudevents.CloudEvent;
|
||||||
|
import io.cloudevents.examples.quarkus.model.User;
|
||||||
|
import io.vertx.core.buffer.Buffer;
|
||||||
|
import io.vertx.core.json.Json;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@Path("/users")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public class UserResource {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);
|
||||||
|
|
||||||
|
@Context
|
||||||
|
UriInfo uriInfo;
|
||||||
|
|
||||||
|
private Map<String, User> users = new HashMap<>();
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{username}")
|
||||||
|
public User get(@PathParam("username") String username) {
|
||||||
|
if (users.containsKey(username)) {
|
||||||
|
return users.get(username);
|
||||||
|
}
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public Map<String, User> list() {
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
public Response create(CloudEvent event) {
|
||||||
|
if (event == null || event.getData() == null) {
|
||||||
|
throw new BadRequestException("Invalid data received. Null or empty event");
|
||||||
|
}
|
||||||
|
User user = Json.decodeValue(Buffer.buffer(event.getData()), User.class);
|
||||||
|
if (users.containsKey(user.getUsername())) {
|
||||||
|
throw new BadRequestException("Username already exists: " + user.getUsername());
|
||||||
|
}
|
||||||
|
LOGGER.info("Received User: {}", user);
|
||||||
|
users.put(user.getUsername(), user);
|
||||||
|
return Response
|
||||||
|
.created(uriInfo.getAbsolutePathBuilder().build(event.getId()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Configuration file
|
||||||
|
# key = value
|
||||||
|
|
||||||
|
## The Rest client will send events to the local UserResource
|
||||||
|
io.cloudevents.examples.quarkus.client.UserClient/mp-rest/url=http://localhost:8080
|
||||||
|
io.cloudevents.examples.quarkus.client.UserClient/mp-rest/scope=javax.inject.Singleton
|
||||||
|
|
||||||
|
quarkus.index-dependency.cloudevents.group-id=io.cloudevents
|
||||||
|
quarkus.index-dependency.cloudevents.artifact-id=cloudevents-http-restful-ws
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package io.cloudevents.examples.quarkus;
|
||||||
|
|
||||||
|
import io.quarkus.test.junit.NativeImageTest;
|
||||||
|
|
||||||
|
@NativeImageTest
|
||||||
|
public class NativeUserResourceTestIT extends UserResourceTest {
|
||||||
|
|
||||||
|
// Execute the same tests but in native mode.
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package io.cloudevents.examples.quarkus;
|
||||||
|
|
||||||
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static io.restassured.RestAssured.given;
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
|
||||||
|
@QuarkusTest
|
||||||
|
public class UserResourceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserEndpoint() {
|
||||||
|
given()
|
||||||
|
.when().get("/users")
|
||||||
|
.then()
|
||||||
|
.statusCode(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue