diff --git a/README.adoc b/README.adoc
index 761fd5578c..e5da8c73c8 100644
--- a/README.adoc
+++ b/README.adoc
@@ -504,6 +504,7 @@ NOTE: Some of these quickstarts use the H2 database included with WildFly. It is
| link:tasks-rs/README{outfilesuffix}[tasks-rs]|JPA, JAX-RS | The `tasks-rs` quickstart demonstrates how to implement a JAX-RS service that uses JPA persistence. | Intermediate | _none_
| link:temperature-converter/README{outfilesuffix}[temperature-converter]|CDI, JSF, SLSB EJB | The `temperature-converter` quickstart does temperature conversion using an EJB Stateless Session Bean (SLSB), CDI, and a JSF front-end client. | Beginner | _none_
| link:thread-racing/README{outfilesuffix}[thread-racing]|Batch, CDI, EE Concurrency, JAX-RS, JMS, JPA, JSON, Web Sockets | A thread racing web application that demonstrates technologies introduced or updated in the latest Jakarta EE specification. | Beginner | _none_
+| link:todo-backend/README{outfilesuffix}[todo-backend]|JPA, JAX-RS, OpenShift, Galleon | The `todo-backend` quickstart demonstrates how to deploy a backend on OpenShift that exposes a HTTP API with JAX-RS and persists data in an external database with JPA | Intermediate | _none_
| link:websocket-client/README{outfilesuffix}[websocket-client]|Web Socket, CDI Events, JSON, SSL | Demonstrates use of a Javascript WebSocket client, WebSocket configuration, programmatic binding, and secure WebSocket. | Intermediate | _none_
| link:websocket-endpoint/README{outfilesuffix}[websocket-endpoint]|CDI, WebSocket, JSON-P | Shows how to use WebSockets with JSON to broadcast information to all open WebSocket sessions in {productName}. | Beginner | _none_
| link:websocket-hello/README{outfilesuffix}[websocket-hello]|WebSocket, CDI, JSF | The `websocket-hello` quickstart demonstrates how to create a simple WebSocket application. | Beginner | _none_
diff --git a/pom.xml b/pom.xml
index a448980306..bc115782f5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,6 +80,9 @@
24.0.0.Beta1-SNAPSHOT
24.0.0.Beta1-SNAPSHOT
+ 23.0.0.Final
+ 4.0.0.Final
+
1.2.0.Beta1
5.4
@@ -448,6 +451,7 @@
tasks-jsf
tasks-rs
temperature-converter
+ todo-backend
thread-racing
websocket-client
websocket-endpoint
diff --git a/todo-backend/README.adoc b/todo-backend/README.adoc
new file mode 100644
index 0000000000..d8cf949367
--- /dev/null
+++ b/todo-backend/README.adoc
@@ -0,0 +1,304 @@
+include::../shared-doc/attributes.adoc[]
+
+= todo-backend: quickstart for backend deployment on OpenShift
+:toc: left
+:icons: font
+:idprefix:
+:idseparator: -
+:keywords: openshift,galleon,helm
+:level: Intermediate
+:technologies: JPA, JAX-RS, OpenShift, Galleon
+
+[abstract]
+The `todo-backend` quickstart demonstrates how to implement a backend that exposes a HTTP API with JAX-RS
+to manage a list of ToDo which are persisted in a database with JPA.
+
+This quickstart shows how to setup a local deployment of this backend as well as a deployment on OpenShift to connect
+to a PostgreSQL database also hosted on OpenShift.
+
+
+== What is it?
+
+The `todo-back` quickstart demonstrates how to implement a backend that exposes a HTTP API with `JAX-RS`
+to manage a list of ToDo which are persisted in a database with `JPA`.
+
+* The backend exposes a HTTP API to manage a list of todos that complies with the specs defined at https://todobackend.com/specs/index.html[todobackend.com].
+* It requires a connection to a PostgreSQL database to persist the todos.
+* It uses the Bootable Jar for local and cloud deployment
+* It is deployed on OpenShift using the https://docs.wildfly.org/wildfly-charts/[Helm Chart for ${productName}].
+
+== Architecture
+
+// System Requirements
+include::../shared-doc/system-requirements.adoc[leveloffset=+1]
+
+This backend is built and deployed as Bootable Jar that provisions the {productName} application server and all the feature packs it needs for its features.
+The layers are defined in the `pom.xml` file in the `` section of the `org.wildfly.plugins:wildfly-jar-maven-plugin` plugin:
+
+[source,xml]
+----
+
+ cloud-server
+ postgresql-datasource
+ ejb
+
+----
+
+The `cloud-server` and `ejb` layers provides everything need to run the backend on OpenShift. This also includes access to
+Jakarta EE APIs such as CDI, JAX-RS, JPA, etc. These two layers comes from the {productName} feature pack provided by the Bootable Jar plugin:
+
+[source,xml]
+----
+
+ wildfly@maven(org.jboss.universe:community-universe)#${version.server.bootable-jar}
+
+----
+
+The `postgresql-datasource` layer provides a JDBC driver and DataSource to connect to a PostgreSQL database. It is not provided
+by the {productName} feature pack but by an extra feature pack:
+
+[source,xml]
+----
+
+ org.wildfly
+ wildfly-datasources-galleon-pack
+ ${version.wildfly-datasources-galleon-pack}
+
+----
+
+The Git repository for this feature pack is hosted at https://github.com/wildfly-extras/wildfly-datasources-galleon-pack.
+It provides JDBC drivers and datasources for different databases but for this quickstart, we will only need the `postgresql-datasource`.
+
+=== Connection to the PostgreSQL database
+
+As mentioned, the JDBC drivers and datasource configuration that the backend uses to connect to the PostgreSQL database
+is provided by the `org.wildfly:wildfly-datasources-galleon-pack` feature pack. By default, it exposes a single datasource.
+In the backend, the name of this datasource is `ToDos` and is specified in the `persistence.xml` to configure JPA:
+
+[source,xml]
+----
+
+ java:jboss/datasources/ToDos
+
+----
+
+At runtime, we only need a few environment variables to establish the connection from {productName} to the external PostgreSQL database:
+
+* `POSTGRESQL_DATABASE` - the name of the database (that will be called `todos`)
+* `POSTGRESQL_SERVICE_HOST` - the host to connect to the database
+* `POSTGRESQL_SERVICE_PORT` - The port to connecto to the database
+* `POSTGRESQL_USER` & `POSTGRESQL_PASSWORD` - the credentials to connect to the database
+* `POSTGRESQL_DATASOURCE` - The name of the datasourcs (as mentioned above, it will be `ToDos`)
+
+=== Filters for Cross-Origin Resource Sharing (CORS)
+
+The Web frontend for this quickstart uses JavaScript calls to query the backend's HTTP API.
+We must enable Cross-Origin Resource Sharing (CORS) filters in the `undertow` subsystem of {productName}.
+
+As we use Bootable Jar to build the application, we provide a CLI script that contains all the commands to
+create and configure the CORS filters in Undertow. This script is located in the `src/scripts/cors_filters.cli`.
+
+This script is executed at build time and will provide the following HTTP headers to enabled CORS:
+
+* `Access-Control-Allow-Origin: *`
+* `Access-Control-Allow-Methods: GET, POST, OPTION, PUT, DELETE, PATCH`
+* `Access-Control-Allow-Headers: accept, authorization, content-type, x-requested-with`
+* `Access-Control-Allow-Credentials: true`
+* `Access-Control-Max-Age: 1`
+
+By default, the backend accepts requests from any origin (`*``). This is only simplicity. It is possible to restrict
+the allowed origin using the environment variable `CORS_ORIGIN` at runtime.
+
+== Package the backend as a Bootable Jar
+
+The backend is packaged as a Bootable Jar and configured to be deployable on OpenShift with the Maven Profile `bootable-jar-openshift` which is active
+by default:
+
+[source,options="nowrap"]
+----
+$ mvn clean package
+----
+
+== Run the backend locally
+
+=== Local PostgreSQL database
+
+Before running the backend locally, we need to have a local PostgreSQL database that we can connect to.
+We use the `postgresql` docker image to create one:
+
+[source,options="nowrap"]
+----
+$ docker run --name todos-db \
+ -e POSTGRES_USER=todos \
+ -e POSTGRES_PASSWORD=mysecretpassword \
+ -p 5432:5432 \
+ postgres
+----
+
+This will create a database named `todos-db` that we can connect to on `localhost:5432` with the credentials `todos / mysecretpassword`.
+
+=== Run the backend
+
+With the PostgreSQL database running, we can start the backend by passing the required environment variables to connect to the database:
+
+[source,options="nowrap"]
+----
+$ POSTGRESQL_DATABASE=todos \
+ POSTGRESQL_SERVICE_HOST=localhost \
+ POSTGRESQL_SERVICE_PORT=5432 \
+ POSTGRESQL_USER=todos \
+ POSTGRESQL_PASSWORD=mysecretpassword \
+ POSTGRESQL_DATASOURCE=ToDos \
+ java -jar target/todo-backend-bootable.jar
+...
+14:41:58,111 INFO [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0010: Deployed "todo-backend.war" (runtime-name : "ROOT.war")
+...
+----
+
+The backend is running, and we can use the HTTP API to manage a list of todos:
+
+[source,options="nowrap"]
+----
+# get a list of todos
+$ curl http://localhost:8080
+[]
+
+# create a todo with the title "This is my first todo item!"
+$ curl -X POST -H "Content-Type: application/json" -d '{"title": "This is my first todo item!"}' http://localhost:8080/
+{"completed":false,"id":1,"order":0,"title":"This is my first todo item!","url":"https://localhost:8080/1"}%
+
+# get a list of todos with the one that was just created
+$ curl http://localhost:8080
+[{"completed":false,"id":1,"order":0,"title":"This is my first todo item!","url":"https://localhost:8080/1"}
+----
+
+== Run the Backend on OpenShift
+
+=== Prerequisites
+
+* You must be logged in OpenShift and have an `oc` client to connect to OpenShift
+* https://helm.sh[Helm] must be installed to deploy the backend on OpenShift.
+
+Once you have installed Helm, you need to add the repository that provides Helm Charts for {productName}:
+
+[source,options="nowrap"]
+----
+$ helm repo add wildfly https://docs.wildfly.org/wildfly-charts/
+"wildfly" has been added to your repositories
+$ helm search repo wildfly
+NAME CHART VERSION APP VERSION DESCRIPTION
+wildfly/wildfly 1.1.0 22.0 Build and Deploy WildFly applications on OpenShift
+----
+
+=== Deploy a PostgreSQL Database on OpenShift
+
+[source,options="nowrap"]
+----
+$ oc new-app postgresql-ephemeral \
+ -p DATABASE_SERVICE_NAME=todos-db \
+ -p POSTGRESQL_DATABASE=todos
+----
+
+This will create a PostgreSQL database named `todos` on OpenShift that can be accessed on the port `5432` on the service `todos-db`.
+We don't need to copy the credentials to connect to the database as we will retrieve them later using the `todos-db` secret that was created
+when the database is deployed.
+
+=== Build and Deploy the Backend on OpenShift
+
+The backend will be built and deployed on OpenShift with a Helm Chart for {productName}.
+
+[source,options="nowrap"]
+----
+$ helm install todo-backend -f todo-backend.yaml wildfly/wildfly
+NAME: todo-backend
+...
+STATUS: deployed
+REVISION: 1
+----
+
+The Helm Chart for this quickstart contains all the information to build an image from the source code using Bootable Jar:
+
+[source,options="nowrap"]
+----
+build:
+ uri: https://github.com/wildfly/quickstart.git
+ mode: bootable-jar
+----
+
+The Helm Chart also contains the environment variables required to connect to the PostgreSQL database.
+In local deployment the credentials were passed directly as the values of the environment variables.
+For OpenShift, we rely on secrets so that the credentials are never copied outside OpenShift:
+
+[source,options="nowrap"]
+----
+deploy:
+ env:
+ - name: POSTGRESQL_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ key: database-password
+ name: todos-db
+----
+
+When the application is deployed, the value for the `POSTGRESQL_PASSWORD` will be taken from the key `database-password`
+in the secrets `todos-db`.
+
+Let's wait for the application to be built and deployed:
+
+[source,options="nowrap"]
+----
+$ oc get dc/todo-backend -w
+NAME REVISION DESIRED CURRENT TRIGGERED BY
+todo-backend 0 3 0 config,image(todo-backend:latest)
+...
+todo-backend 1 3 3 config,image(todo-backend:latest)
+----
+
+=== Use the todobackend Web application
+
+Once the backend is deployed on OpenShift, it can be accessed from the route `todo-backend`.
+Let's find the host that we can use to connect to this backend:
+
+[source,options="nowrap"]
+----
+$ oc get route todo-backend -o jsonpath="{.spec.host}"
+todo-backend-jmesnil1-dev.apps.sandbox.x8i5.p1.openshiftapps.com
+----
+
+This value will be different for every installation of the backend.
+
+[WARNING]
+====
+Make sure to prepend the host with `https://` to be able to connect to the backend from the ToDo Backend Specs or Client
+====
+
+We can verify that this application is properly working as a ToDo Backend by running its https://todobackend.com/specs/index.html[specs] on it.
+
+
+Once all tests passed, we can use the https://todobackend.com/client/index.html[todobackend client] to have a Web application connected to the backend.
+
+=== Clean up
+
+==== Remove the Backend
+
+The backend can be deleted from OpenShift by running the command:
+
+[source,options="nowrap"]
+----
+$ helm delete todo-backend
+release "todo-backend" uninstalled
+----
+
+==== Remove the Database
+
+The PostresSQL database can be deleted from OpenShift by running the commands:
+
+[source,options="nowrap"]
+----
+$ oc delete all -l template=postgresql-ephemeral-template
+replicationcontroller "todos-db-1" deleted
+service "todos-db" deleted
+deploymentconfig.apps.openshift.io "todos-db" deleted
+$ oc delete secret todos-db
+secret "todos-db" deleted
+----
\ No newline at end of file
diff --git a/todo-backend/pom.xml b/todo-backend/pom.xml
new file mode 100644
index 0000000000..608024d60d
--- /dev/null
+++ b/todo-backend/pom.xml
@@ -0,0 +1,137 @@
+
+
+
+ 4.0.0
+
+ org.wildfly.quickstarts
+ quickstart-parent
+
+ 24.0.0.Beta1-SNAPSHOT
+ ../pom.xml
+
+
+ todo-backend
+ war
+ Quickstart: Todo backend
+ This project demonstrates how to implement a Todo-Backend application to manage ToDos
+
+
+
+ Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.html
+ repo
+
+
+
+
+ 1.2.3.Final
+
+
+
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
+ provided
+
+
+ jakarta.persistence
+ jakarta.persistence-api
+ provided
+
+
+ org.jboss.spec.javax.ejb
+ jboss-ejb-api_3.2_spec
+ provided
+
+
+ org.jboss.spec.javax.ws.rs
+ jboss-jaxrs-api_2.1_spec
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-jackson2-provider
+ provided
+
+
+
+
+
+ ${project.artifactId}
+
+
+
+
+ bootable-jar-openshift
+
+ true
+
+
+
+
+ org.wildfly.plugins
+ wildfly-jar-maven-plugin
+ ${version.wildfly-jar.maven.plugin}
+
+
+
+ wildfly@maven(org.jboss.universe:community-universe)#${version.server.bootable-jar}
+
+
+ org.wildfly
+ wildfly-datasources-galleon-pack
+ ${version.wildfly-datasources-galleon-pack}
+
+
+
+
+ cloud-server
+ postgresql-datasource
+ ejb
+
+
+
+
+
+ false
+
+
+
+
+
+
+ true
+
+
+
+
+
+ package
+
+
+
+
+
+
+
+
+
diff --git a/todo-backend/src/main/java/org/wildfly/quickstarts/todos/Resources.java b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/Resources.java
new file mode 100644
index 0000000000..19de930299
--- /dev/null
+++ b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/Resources.java
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2020, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.wildfly.quickstarts.todos;
+
+import javax.ejb.Stateful;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.inject.Produces;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.PersistenceContextType;
+
+@Stateful
+@RequestScoped
+public class Resources {
+
+ @PersistenceContext(type = PersistenceContextType.EXTENDED)
+ private EntityManager em;
+
+ @Produces
+ public EntityManager getEm() {
+ return em;
+ }
+}
\ No newline at end of file
diff --git a/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDo.java b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDo.java
new file mode 100644
index 0000000000..7b6a078c76
--- /dev/null
+++ b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDo.java
@@ -0,0 +1,143 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2020, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.wildfly.quickstarts.todos;
+
+import static java.util.Optional.ofNullable;
+import static javax.persistence.GenerationType.IDENTITY;
+
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Objects;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.ws.rs.core.UriBuilder;
+
+@Entity
+public class ToDo {
+
+ @Id
+ @GeneratedValue(strategy = IDENTITY)
+ private Long id;
+ private String title;
+ private Boolean completed = false;
+ @Column(name = "\"order\"")
+ private int order;
+ private URL url;
+
+ public ToDo() {
+
+ }
+
+ public ToDo(String title) {
+ this.title = title;
+ }
+
+ public ToDo(Long id, String title, Boolean completed, int order, URL url) {
+ this.id = id;
+ this.title = title;
+ this.completed = completed;
+ this.order = order;
+ this.url = url;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public boolean isCompleted() {
+ return completed;
+ }
+
+ public void setCompleted(Boolean completed) {
+ this.completed = completed;
+ }
+
+ public Boolean getCompleted(){
+ return this.completed == null ? false : this.completed;
+ }
+
+ public int getOrder() {
+ return order;
+ }
+
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ public URL getUrl() throws MalformedURLException, URISyntaxException {
+ if (this.id != null) {
+ return UriBuilder.fromUri(url.toURI()).scheme(url.getProtocol()).path(id.toString()).build().toURL();
+ }
+ return this.url;
+ }
+
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ public void update(ToDo newTodo) {
+ this.title = ofNullable(newTodo.title).orElse(title);
+ this.completed = ofNullable(newTodo.completed).orElse(completed);
+ this.order = ofNullable(newTodo.order).orElse(order);
+ this.url = ofNullable(newTodo.url).orElse(url);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ToDo toDo = (ToDo) o;
+ return id == toDo.id;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ @Override
+ public String toString() {
+ return "ToDo{" +
+ "id=" + id +
+ ", title='" + title + '\'' +
+ ", completed=" + completed +
+ ", order=" + order +
+ ", url=" + url +
+ '}';
+ }
+}
diff --git a/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoBackendApplication.java b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoBackendApplication.java
new file mode 100644
index 0000000000..4c8488cae7
--- /dev/null
+++ b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoBackendApplication.java
@@ -0,0 +1,29 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2020, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.wildfly.quickstarts.todos;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+@ApplicationPath("")
+public class ToDoBackendApplication extends Application{
+}
diff --git a/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoController.java b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoController.java
new file mode 100644
index 0000000000..86937a9f54
--- /dev/null
+++ b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoController.java
@@ -0,0 +1,106 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2020, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.wildfly.quickstarts.todos;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import java.net.MalformedURLException;
+import java.util.List;
+import java.util.Optional;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.transaction.Transactional;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.PATCH;
+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.Response;
+import javax.ws.rs.core.UriInfo;
+
+@Path("")
+@RequestScoped
+public class ToDoController {
+
+ @Inject
+ private ToDoDAO todoDAO;
+
+ @Context
+ private UriInfo uriInfo;
+
+ @GET
+ @Produces(APPLICATION_JSON)
+ public List getAllTodos(){
+ return todoDAO.findAll();
+ }
+
+ @GET
+ @Path("{id}")
+ @Produces(APPLICATION_JSON)
+ public ToDo getTodoFrom(@PathParam("id") long id) {
+ Optional todo = todoDAO.findById(id);
+ return todo.orElseThrow(() -> new NotFoundException("ToDo does not exist!"));
+ }
+
+ @DELETE
+ @Transactional
+ public void deleteAllTodos(){
+ todoDAO.findAll().forEach(todo -> todoDAO.remove(todo));
+ }
+
+ @DELETE
+ @Path("{id}")
+ @Transactional
+ public Response deleteTodoFrom(@PathParam("id") long id) {
+ Optional optional = todoDAO.findById(id);
+ ToDo todo = optional.orElseThrow(() -> new NotFoundException("ToDo does not exist!"));
+ todoDAO.remove(todo);
+ return Response.ok().build();
+ }
+
+ @POST
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON)
+ @Transactional
+ public ToDo addTodo(ToDo todo) throws MalformedURLException {
+ todo.setUrl(uriInfo.getAbsolutePathBuilder().scheme("https").build().toURL());
+ todoDAO.insert(todo);
+ return todo;
+ }
+
+ @PATCH
+ @Path("{id}")
+ @Produces(APPLICATION_JSON)
+ @Consumes(APPLICATION_JSON)
+ @Transactional
+ public ToDo updateTodo(@PathParam("id") long id, ToDo update) {
+ System.out.println("ToDoController.updateTodo");
+ System.out.println("id = " + id + ", update = " + update);
+ return todoDAO.update(id, update).orElseThrow(() -> new NotFoundException("ToDo does not exist!"));
+ }
+}
\ No newline at end of file
diff --git a/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoDAO.java b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoDAO.java
new file mode 100644
index 0000000000..1eaff4ab29
--- /dev/null
+++ b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoDAO.java
@@ -0,0 +1,41 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2020, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.wildfly.quickstarts.todos;
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.ejb.Local;
+
+@Local
+public interface ToDoDAO {
+
+ List findAll();
+
+ Optional findById(Long id);
+
+ void remove(ToDo todo);
+
+ void insert(ToDo todo);
+
+ Optional update(Long id, ToDo todo);
+}
\ No newline at end of file
diff --git a/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoDAOImpl.java b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoDAOImpl.java
new file mode 100644
index 0000000000..5e2dd88b53
--- /dev/null
+++ b/todo-backend/src/main/java/org/wildfly/quickstarts/todos/ToDoDAOImpl.java
@@ -0,0 +1,81 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2020, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.wildfly.quickstarts.todos;
+
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.ejb.Stateful;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+@Stateful
+public class ToDoDAOImpl implements ToDoDAO {
+
+ @Inject
+ private EntityManager em;
+
+ @Context
+ UriInfo uriInfo;
+
+ @Override
+ public List findAll() {
+ TypedQuery query = em.createQuery("FROM ToDo", ToDo.class);
+ return query.getResultList();
+ }
+
+ public Optional findById(Long id) {
+ System.out.println("ToDoDAOImpl.findById");
+ System.out.println("id = " + id);
+ ToDo toDo = em.find(ToDo.class, id);
+ System.out.println("toDo = " + toDo);
+ return Optional.ofNullable(toDo);
+ }
+
+ @Override
+ public void remove(ToDo todo) {
+ em.remove(todo);
+ }
+
+ @Override
+ public void insert(ToDo todo) {
+ em.persist(todo);
+ }
+
+ @Override
+ public Optional update(Long id, ToDo newTodo) {
+ System.out.println("ToDoDAOImpl.update");
+ System.out.println("id = " + id + ", newTodo = " + newTodo);
+ Optional optional = findById(id);
+ System.out.println("optional before update= " + optional);
+ if (optional.isPresent()) {
+ optional.get().update(newTodo);
+ System.out.println("optional after update= " + optional);
+ return optional;
+ }
+ return Optional.empty();
+ }
+}
\ No newline at end of file
diff --git a/todo-backend/src/main/resources/META-INF/persistence.xml b/todo-backend/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000000..a9d9e54e20
--- /dev/null
+++ b/todo-backend/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,31 @@
+
+
+
+
+ java:jboss/datasources/ToDos
+
+
+
+
+
+
+
diff --git a/todo-backend/src/main/scripts/cors_filters.cli b/todo-backend/src/main/scripts/cors_filters.cli
new file mode 100644
index 0000000000..12b029fbba
--- /dev/null
+++ b/todo-backend/src/main/scripts/cors_filters.cli
@@ -0,0 +1,16 @@
+echo Adding Undertow Filters for CORS
+# Access-Control-Allow-Origin
+/subsystem=undertow/server=default-server/host=default-host/filter-ref="Access-Control-Allow-Origin":add()
+/subsystem=undertow/configuration=filter/response-header="Access-Control-Allow-Origin":add(header-name="Access-Control-Allow-Origin",header-value="${env.CORS_ORIGIN:*}")
+# Access-Control-Allow-Methods
+/subsystem=undertow/server=default-server/host=default-host/filter-ref="Access-Control-Allow-Methods":add()
+/subsystem=undertow/configuration=filter/response-header="Access-Control-Allow-Methods":add(header-name="Access-Control-Allow-Methods",header-value="GET, POST, OPTION, PUT, DELETE, PATCH")
+# Access-Control-Allow-Headers
+/subsystem=undertow/server=default-server/host=default-host/filter-ref="Access-Control-Allow-Headers":add()
+/subsystem=undertow/configuration=filter/response-header="Access-Control-Allow-Headers":add(header-name="Access-Control-Allow-Headers",header-value="accept, authorization, content-type, x-requested-with")
+# Access-Control-Allow-Credentials
+/subsystem=undertow/server=default-server/host=default-host/filter-ref="Access-Control-Allow-Credentials":add()
+/subsystem=undertow/configuration=filter/response-header="Access-Control-Allow-Credentials":add(header-name="Access-Control-Allow-Credentials",header-value="true")
+# Access-Control-Max-Age
+/subsystem=undertow/server=default-server/host=default-host/filter-ref="Access-Control-Max-Age":add()
+/subsystem=undertow/configuration=filter/response-header="Access-Control-Max-Age":add(header-name="Access-Control-Max-Age",header-value="1")
\ No newline at end of file
diff --git a/todo-backend/src/main/webapp/WEB-INF/beans.xml b/todo-backend/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000000..d3ddf148e7
--- /dev/null
+++ b/todo-backend/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/todo-backend/todo-backend.yaml b/todo-backend/todo-backend.yaml
new file mode 100644
index 0000000000..c1c153911c
--- /dev/null
+++ b/todo-backend/todo-backend.yaml
@@ -0,0 +1,43 @@
+# This configuration file can be used to build and deploy the todo-backend
+# quickstart on OpenShift with the Helm Chart for WildFly.
+build:
+ uri: https://github.com/quickstart/quickstart.git
+ ref: todo-backend
+ mode: bootable-jar
+ env:
+ - name: ARTIFACT_DIR
+ value: todo-backend/target
+ - name: MAVEN_ARGS_APPEND
+ value: -am -pl todo-backend
+ - name: MAVEN_OPTS
+ value: '-XX:MetaspaceSize=96m -XX:MaxMetaspaceSize=256m'
+deploy:
+ replicas: 3
+ env:
+ # Env vars to connect to PostgreSQL DB
+ - name: POSTGRESQL_DATABASE
+ valueFrom:
+ secretKeyRef:
+ key: database-name
+ name: todos-db
+ - name: POSTGRESQL_USER
+ valueFrom:
+ secretKeyRef:
+ key: database-user
+ name: todos-db
+ - name: POSTGRESQL_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ key: database-password
+ name: todos-db
+ - name: POSTGRESQL_DATASOURCE
+ value: ToDos
+ - name: POSTGRESQL_SERVICE_HOST
+ value: todos-db
+ - name: POSTGRESQL_SERVICE_PORT
+ value: "5432"
+ # Env to avoid OOME
+ - name: GC_MAX_METASPACE_SIZE
+ value: "256"
+ - name: GC_METASPACE_SIZE
+ value: "96"