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