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>
|
||||
<module>kafka</module>
|
||||
<module>restful-ws-quarkus</module>
|
||||
<module>vertx</module>
|
||||
</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