From 29b00cb86034721186edeb9598c5933bc4710ad6 Mon Sep 17 00:00:00 2001 From: aberinnj Date: Thu, 5 Sep 2024 16:21:37 -0500 Subject: [PATCH 01/16] Create serial-config.properties --- .../src/main/resources/META-INF/serial-config.properties | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 mtdrworkshop/backend/src/main/resources/META-INF/serial-config.properties diff --git a/mtdrworkshop/backend/src/main/resources/META-INF/serial-config.properties b/mtdrworkshop/backend/src/main/resources/META-INF/serial-config.properties new file mode 100644 index 0000000..7890013 --- /dev/null +++ b/mtdrworkshop/backend/src/main/resources/META-INF/serial-config.properties @@ -0,0 +1,2 @@ +# https://github.com/helidon-io/helidon/issues/7000 +pattern=oracle.sql.converter.*;oracle.i18n.text.converter.* \ No newline at end of file From f7f3794f778ec472a2dd920b9081adb54954194d Mon Sep 17 00:00:00 2001 From: aberinnj Date: Thu, 5 Sep 2024 16:37:09 -0500 Subject: [PATCH 02/16] Update pom.xml --- mtdrworkshop/backend/pom.xml | 53 ++++++++++++++---------------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/mtdrworkshop/backend/pom.xml b/mtdrworkshop/backend/pom.xml index 377814c..4e9da00 100644 --- a/mtdrworkshop/backend/pom.xml +++ b/mtdrworkshop/backend/pom.xml @@ -1,9 +1,9 @@ - + - com.oracle.oci.sdk - oci-java-sdk-common - 1.32.2 + com.oracle.database.jdbc + ojdbc11 + 23.4.0.24.05 - + + com.oracle.database.jdbc + ucp11 + 23.4.0.24.05 + + + com.oracle.database.security + oraclepki + 23.4.0.24.05 + + + + io.helidon.webserver helidon-webserver-cors @@ -126,31 +138,6 @@ io.helidon.health helidon-health-checks - - com.oracle.ojdbc - ojdbc10 - 19.3.0.0 - - - com.oracle.ojdbc - ucp - 19.3.0.0 - - - com.oracle.ojdbc - osdt_core - 19.3.0.0 - - - com.oracle.ojdbc - osdt_cert - 19.3.0.0 - - - com.oracle.ojdbc - oraclepki - 19.3.0.0 - From eea3d1b0ab4674d2bf2058174c96557d56cf2daf Mon Sep 17 00:00:00 2001 From: aberinnj Date: Thu, 5 Sep 2024 18:43:24 -0500 Subject: [PATCH 03/16] Added appdatasource --- .../com/oracle/database/AppDatasource.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 mtdrworkshop/backend/src/main/java/com/oracle/database/AppDatasource.java diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/database/AppDatasource.java b/mtdrworkshop/backend/src/main/java/com/oracle/database/AppDatasource.java new file mode 100644 index 0000000..1197f4a --- /dev/null +++ b/mtdrworkshop/backend/src/main/java/com/oracle/database/AppDatasource.java @@ -0,0 +1,72 @@ +package com.oracle.database; + +import io.helidon.config.Config; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public class AppDatasource { + + // connection pooling using UCP + private final PoolDataSource pds; + private static AppDatasource instance = null; + + // static get + public synchronized static AppDatasource get(Config c) { + if (instance == null) { + instance = new AppDatasource(c); + } + return instance; + } + + // constructor, initialize datasource + private AppDatasource(Config c) { + this.pds = PoolDataSourceFactory.getPoolDataSource(); + String url = c.get("url").asString().orElse(""); + String username = c.get("user").asString().orElse(""); + String password = c.get("password").asString().orElse(""); + + try { + + // In this application, we don't set any init, min or max size in UCP. We + // also don't start the pool explicitly. This means that the very first + // connection request will start the pool. The default maximum pool size + // is MAX_INT which isn't appropriate and should be configured properly in + // production. + this.pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + this.pds.setConnectionPoolName("JDBC_UCP_POOL"); + this.pds.setInactiveConnectionTimeout(60); + this.pds.setMaxStatements(10); + + // if provided, set + if (!url.isEmpty()) { + this.pds.setURL(url); + } + if (!username.isEmpty()) { + this.pds.setUser(username); + } + if (!password.isEmpty()) { + this.pds.setPassword(password); + } + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + // retrieve connection + public Connection getConnection() throws SQLException { + return this.pds.getConnection(); + } + + + + + + + + +} From 7f75e395b6d5edb64ad893c5d01bf87001720796 Mon Sep 17 00:00:00 2001 From: aberinnj Date: Thu, 5 Sep 2024 18:44:19 -0500 Subject: [PATCH 04/16] Added serial-config --- .../main/resources/META-INF/helidon/serial-config.properties | 3 +++ .../src/main/resources/META-INF/serial-config.properties | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 mtdrworkshop/backend/src/main/resources/META-INF/helidon/serial-config.properties delete mode 100644 mtdrworkshop/backend/src/main/resources/META-INF/serial-config.properties diff --git a/mtdrworkshop/backend/src/main/resources/META-INF/helidon/serial-config.properties b/mtdrworkshop/backend/src/main/resources/META-INF/helidon/serial-config.properties new file mode 100644 index 0000000..f2c7e73 --- /dev/null +++ b/mtdrworkshop/backend/src/main/resources/META-INF/helidon/serial-config.properties @@ -0,0 +1,3 @@ +# https://github.com/helidon-io/helidon/issues/7000 +pattern=oracle.sql.converter.* + diff --git a/mtdrworkshop/backend/src/main/resources/META-INF/serial-config.properties b/mtdrworkshop/backend/src/main/resources/META-INF/serial-config.properties deleted file mode 100644 index 7890013..0000000 --- a/mtdrworkshop/backend/src/main/resources/META-INF/serial-config.properties +++ /dev/null @@ -1,2 +0,0 @@ -# https://github.com/helidon-io/helidon/issues/7000 -pattern=oracle.sql.converter.*;oracle.i18n.text.converter.* \ No newline at end of file From 56c8a3e4e0342585dc121f616237b2442edf18c7 Mon Sep 17 00:00:00 2001 From: aberinnj Date: Thu, 5 Sep 2024 18:45:15 -0500 Subject: [PATCH 05/16] Updated database access to support other methods of connecting --- .../main/java/com/oracle/todoapp/Main.java | 6 +- .../com/oracle/todoapp/TodoItemStorage.java | 68 +++++-------------- .../oracle/todoapp/TodoListAppService.java | 2 +- 3 files changed, 22 insertions(+), 54 deletions(-) diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java index c3acbfc..a2644ea 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java @@ -19,6 +19,7 @@ import io.helidon.webserver.cors.CrossOriginConfig; + /* * This is the helidon-se backend. * @author jean.de.lavarene@oracle.com @@ -29,13 +30,14 @@ public static void main(final String[] args) throws IOException, SQLException { System.out.println("Working Directory = " + System.getProperty("user.dir")); System.setProperty("oracle.jdbc.fanEnabled", "false"); + LogManager .getLogManager() .readConfiguration( Main.class.getResourceAsStream("/logging.properties")); Config config = Config.create(); - WebServer.builder() + WebServer.builder() .config(config.get("server")) //update this server configuration from the config provided .addMediaSupport(JsonpSupport.create()) .routing(createRouting(config)) @@ -54,7 +56,7 @@ public static void main(final String[] args) }); } - private static Routing createRouting(Config config) throws SQLException { + private static Routing createRouting(Config config) { // Creates a Helidon's Service implementation. // Use database configuration from application.yaml that diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java index a43c261..410d732 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java @@ -1,5 +1,5 @@ /* -## MyToDoReact version 2.0.0 +## MyToDoReact version 2.1.0 ## ## Copyright (c) 2021 Oracle, Inc. ## Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ @@ -18,10 +18,9 @@ import java.util.logging.Level; import java.util.logging.Logger; +import com.oracle.database.AppDatasource; import io.helidon.config.Config; -import oracle.ucp.jdbc.PoolDataSource; -import oracle.ucp.jdbc.PoolDataSourceFactory; -//import io.helidon.dbclient.jdbc; + /* * This class takes care of the storage of the todo items. It uses an Autonomous Database * from the Oracle Cloud (ATP). The following table is used to store the todo items: @@ -38,47 +37,14 @@ */ class TodoItemStorage { - // grabbing the dbpassword from the kubernetes secret, added by peter song - static String pwSecretFromK8s = System.getenv("dbpassword").trim(); - private final static Logger LOGGER = Logger.getLogger(TodoItemStorage.class.getName()); - private final PoolDataSource pool; - - // singleton repository: - private static TodoItemStorage storage = null; + private final static Logger LOGGER = Logger.getLogger(TodoItemStorage.class.getName()); - synchronized static TodoItemStorage get(Config config) { - if(storage == null) { - try { - storage = new TodoItemStorage(config); - } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, ()->config.toString()); - LOGGER.log(Level.SEVERE, ex, ()->"in TodoItemRepository.get(...)"); - } - } - return storage; - } + private final AppDatasource ads; - private TodoItemStorage(Config config) throws SQLException { - LOGGER.log(Level.CONFIG, ()->config.toString()); - // trying this in place of "url and user and dbpasswor" - //String password = config.get("password").asString().get(); - String url = config.get("url").asString().get(); - String user = config.get("user").asString().get(); - System.out.printf("Using url: %s%n", url); - pool = PoolDataSourceFactory.getPoolDataSource(); - pool.setURL(url); - //pool.setUser(user); - pool.setUser("TODOUSER"); - pool.setPassword(pwSecretFromK8s); - pool.setInactiveConnectionTimeout(60); - pool.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); - pool.setMaxStatements(10); - // In this application, we don't set any init, min or max size in UCP. We - // also don't start the pool explicitly. This means that the very first - // connection request will start the pool. The default maximum pool size - // is MAX_INT which isn't appropriate and should be configured properly in - // production. + TodoItemStorage(Config config) { + LOGGER.log(Level.CONFIG, config::toString); + ads = AppDatasource.get(config); } /** @@ -100,8 +66,8 @@ List all() { LOGGER.fine("all"); ArrayList list = new ArrayList(); try ( - Connection conn = pool.getConnection(); - PreparedStatement pstmt = conn.prepareStatement("SELECT id, description, creation_ts, done FROM todoitem ORDER BY creation_ts DESC"); + Connection conn = ads.getConnection(); + PreparedStatement pstmt = conn.prepareStatement("SELECT id, description, creation_ts, done FROM TODOOWNER.todoitem ORDER BY creation_ts DESC"); ResultSet rs = pstmt.executeQuery(); ) { while(rs.next()) { @@ -124,8 +90,8 @@ TodoItem getById(int id) { LOGGER.fine(()->"getById("+id+")"); TodoItem ret = null; try ( - Connection conn = pool.getConnection(); - PreparedStatement pstmt = conn.prepareStatement("SELECT id, description, creation_ts, done FROM todoitem WHERE id=?"); + Connection conn = ads.getConnection(); + PreparedStatement pstmt = conn.prepareStatement("SELECT id, description, creation_ts, done FROM todoowner.todoitem WHERE id=?"); ){ pstmt.setInt(1, id); try (ResultSet rs = pstmt.executeQuery();) { @@ -163,11 +129,11 @@ TodoItem save(TodoItem item) { LOGGER.fine("save("+item.toString()+")"); int idInt = item.getId(); try ( - Connection conn = pool.getConnection(); + Connection conn = ads.getConnection(); ){ if(idInt >=0 ) { try( - PreparedStatement pstmt = conn.prepareStatement("UPDATE todoitem SET description=?, done=? WHERE id=?"); + PreparedStatement pstmt = conn.prepareStatement("UPDATE todoowner.todoitem SET description=?, done=? WHERE id=?"); ){ pstmt.setString(1, item.getDescription()); pstmt.setInt(2, item.isDone()?1:0); @@ -178,7 +144,7 @@ TodoItem save(TodoItem item) { return this.getById(idInt); } else { try ( - PreparedStatement pstmt = conn.prepareStatement("INSERT INTO todoitem (description) VALUES (?)", new String[]{"id"}); + PreparedStatement pstmt = conn.prepareStatement("INSERT INTO todoowner.todoitem (description) VALUES (?)", new String[]{"id"}); ){ pstmt.setString(1, item.getDescription()); @@ -211,8 +177,8 @@ boolean deleteById(int id) { LOGGER.fine(()->"deleteById(" + id + ")"); int ret = 0; try ( - Connection conn = pool.getConnection(); - PreparedStatement pstmt = conn.prepareStatement("DELETE FROM todoitem WHERE id=?"); + Connection conn = ads.getConnection(); + PreparedStatement pstmt = conn.prepareStatement("DELETE FROM todoowner.todoitem WHERE id=?"); ){ pstmt.setInt(1, id); ret = pstmt.executeUpdate(); diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoListAppService.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoListAppService.java index 521ecfc..7ac65da 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoListAppService.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoListAppService.java @@ -29,7 +29,7 @@ public class TodoListAppService implements Service { private final TodoItemStorage todoItems; public TodoListAppService(Config config) { - todoItems = TodoItemStorage.get(config); + todoItems = new TodoItemStorage(config); } @Override From 58d5669d070beee212127492676ea1881d7835e0 Mon Sep 17 00:00:00 2001 From: aberinnj Date: Thu, 5 Sep 2024 19:48:48 -0500 Subject: [PATCH 06/16] Updated manifest to support new environment variables --- mtdrworkshop/backend/src/main/k8s/manifest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mtdrworkshop/backend/src/main/k8s/manifest.yaml b/mtdrworkshop/backend/src/main/k8s/manifest.yaml index dbaaad5..c0475ca 100644 --- a/mtdrworkshop/backend/src/main/k8s/manifest.yaml +++ b/mtdrworkshop/backend/src/main/k8s/manifest.yaml @@ -30,7 +30,7 @@ spec: value: "todoitem" - name: OCI_REGION value: "%OCI_REGION%" - - name: dbpassword + - name: database.password valueFrom: secretKeyRef: name: dbuser From 1c6ecee7dca49d5ccbac8c1ccbdef4724f3304c9 Mon Sep 17 00:00:00 2001 From: aberinnj Date: Thu, 5 Sep 2024 19:49:52 -0500 Subject: [PATCH 07/16] Removing target files --- .../backend/target/classes/META-INF/beans.xml | 49 ------------------- .../backend/target/classes/logging.properties | 32 ------------ .../backend/target/classes/wallet/.gitkeep | 1 - .../target/maven-archiver/pom.properties | 3 -- .../compile/default-compile/createdFiles.lst | 5 -- .../compile/default-compile/inputFiles.lst | 5 -- 6 files changed, 95 deletions(-) delete mode 100644 mtdrworkshop/backend/target/classes/META-INF/beans.xml delete mode 100644 mtdrworkshop/backend/target/classes/logging.properties delete mode 100644 mtdrworkshop/backend/target/classes/wallet/.gitkeep delete mode 100644 mtdrworkshop/backend/target/maven-archiver/pom.properties delete mode 100644 mtdrworkshop/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst delete mode 100644 mtdrworkshop/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/mtdrworkshop/backend/target/classes/META-INF/beans.xml b/mtdrworkshop/backend/target/classes/META-INF/beans.xml deleted file mode 100644 index 095efb9..0000000 --- a/mtdrworkshop/backend/target/classes/META-INF/beans.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - diff --git a/mtdrworkshop/backend/target/classes/logging.properties b/mtdrworkshop/backend/target/classes/logging.properties deleted file mode 100644 index b7c726a..0000000 --- a/mtdrworkshop/backend/target/classes/logging.properties +++ /dev/null @@ -1,32 +0,0 @@ -# MyToDoReact version 1.0. -## -## Copyright (c) 2021 Oracle, Inc. -## Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ - -# Example Logging Configuration File -# For more information see $JAVA_HOME/jre/lib/logging.properties - -# Send messages to the console -handlers=java.util.logging.ConsoleHandler - -# Global default logging level. Can be overriden by specific handlers and loggers -.level=INFO - -# Helidon Web Server has a custom log formatter that extends SimpleFormatter. -# It replaces "!thread!" with the current thread name -java.util.logging.ConsoleHandler.level=FINE -java.util.logging.ConsoleHandler.formatter=io.helidon.webserver.netty.WebServerLogFormatter -java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n - -#Component specific log levels -com.example.todolist.level = FINE -io.helidon.webserver.level=INFO -io.helidon.config.level=INFO -io.helidon.security.level=INFO -io.helidon.microprofile.level=INFO -io.helidon.common.level=INFO -io.netty.level=INFO -org.glassfish.jersey.level=INFO -org.jboss.weld=INFO - -com.oracle.todoapp.level=ALL diff --git a/mtdrworkshop/backend/target/classes/wallet/.gitkeep b/mtdrworkshop/backend/target/classes/wallet/.gitkeep deleted file mode 100644 index 8b13789..0000000 --- a/mtdrworkshop/backend/target/classes/wallet/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/mtdrworkshop/backend/target/maven-archiver/pom.properties b/mtdrworkshop/backend/target/maven-archiver/pom.properties deleted file mode 100644 index df912bc..0000000 --- a/mtdrworkshop/backend/target/maven-archiver/pom.properties +++ /dev/null @@ -1,3 +0,0 @@ -groupId=io.helidon -artifactId=todolistapp-helidon-se -version=1.0-SNAPSHOT diff --git a/mtdrworkshop/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/mtdrworkshop/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst deleted file mode 100644 index d98dffa..0000000 --- a/mtdrworkshop/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ /dev/null @@ -1,5 +0,0 @@ -com/oracle/todoapp/TodoItemNotFoundException.class -com/oracle/todoapp/TodoListAppService.class -com/oracle/todoapp/TodoItem.class -com/oracle/todoapp/Main.class -com/oracle/todoapp/TodoItemStorage.class diff --git a/mtdrworkshop/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/mtdrworkshop/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst deleted file mode 100644 index 4b882ce..0000000 --- a/mtdrworkshop/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ /dev/null @@ -1,5 +0,0 @@ -/Users/jean/Dropbox/Jean/Oracle/OracleCloud/TodoListApp/todolistapp-helidon-se/src/main/java/com/oracle/todoapp/TodoListAppService.java -/Users/jean/Dropbox/Jean/Oracle/OracleCloud/TodoListApp/todolistapp-helidon-se/src/main/java/com/oracle/todoapp/TodoItemStorage.java -/Users/jean/Dropbox/Jean/Oracle/OracleCloud/TodoListApp/todolistapp-helidon-se/src/main/java/com/oracle/todoapp/TodoItem.java -/Users/jean/Dropbox/Jean/Oracle/OracleCloud/TodoListApp/todolistapp-helidon-se/src/main/java/com/oracle/todoapp/TodoItemNotFoundException.java -/Users/jean/Dropbox/Jean/Oracle/OracleCloud/TodoListApp/todolistapp-helidon-se/src/main/java/com/oracle/todoapp/Main.java From 3e55afea379bbc34eb07330da72e561fab00af80 Mon Sep 17 00:00:00 2001 From: aberinnj Date: Fri, 6 Sep 2024 07:21:11 -0500 Subject: [PATCH 08/16] Sample SQL file --- mtdrworkshop/backend/sql/todoitem.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 mtdrworkshop/backend/sql/todoitem.sql diff --git a/mtdrworkshop/backend/sql/todoitem.sql b/mtdrworkshop/backend/sql/todoitem.sql new file mode 100644 index 0000000..eca8375 --- /dev/null +++ b/mtdrworkshop/backend/sql/todoitem.sql @@ -0,0 +1,10 @@ + + +CREATE TABLE TODOOWNER.TODOITEM ( + id NUMBER GENERATED ALWAYS AS IDENTITY, description VARCHAR2(4000), + creation_ts TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + done NUMBER(1,0) default 0, + PRIMARY KEY (id) +); + +insert into TODOOWNER.todoitem (description) values ('My first task!'); \ No newline at end of file From 82ef51eee702b46d1d64c28fb6ff839c3b896f1e Mon Sep 17 00:00:00 2001 From: aberinnj Date: Fri, 6 Sep 2024 07:21:28 -0500 Subject: [PATCH 09/16] Added README --- mtdrworkshop/backend/README.md | 112 +++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 mtdrworkshop/backend/README.md diff --git a/mtdrworkshop/backend/README.md b/mtdrworkshop/backend/README.md new file mode 100644 index 0000000..fc956dc --- /dev/null +++ b/mtdrworkshop/backend/README.md @@ -0,0 +1,112 @@ +# Todolist Helidon backend + +Todolist application backend built with Helidon SE, using Oracle JDBC +- __App Version `v2.1.0`__ +- __Oracle JDBC Version `v23.4.0.24.05`__ +- __Helidon SE Version `v2.4.2`__ + +## Environment Variables +The following environment variables are expected by the application. +In order to successfully run the application, the environment variables below are __REQUIRED__. + +| Variable | Name | Default | Description | +|---------------------|-------------------|---------|-----------------------------------------------------------------| +| `database.url` | Database URL | - | Connection to URL, in the form of `jdbc:oracle:thin:@
` | +| `database.user` | Database User | - | Database user with access to the necessary tables | +| `database.password` | Database Password | - | Database user credentials | + +## API Endpoints + +The following endpoints are endpoints used by the application. + +| Method | REST Endpoint | Sample Data | Description | +|--------|-------------------------------------------|----------------------------------------|-----------------------| +| GET | `http://localhost:8080/api/todolist` | - | Retrieves all Todos | +| POST | `http://localhost:8080/api/todolist` | `{"description" : "Second new task!"}` | Saves a new Todo | +| GET | `http://localhost:8080/api/todolist/{id}` | - | Retrieves a Todo item | +| PUT | `http://localhost:8080/api/todolist/{id}` | `{"description": "...", "done": true}` | Updates a Todo item | +| DELETE | `http://localhost:8080/api/todolist/{id}` | - | Deletes a Todo item | + + +## SQL Schema, Tables and Queries + +The application expects and makes use of the following: + +- __Database Schemas__: `TODOOWNER` +- __Database Tables__: `TODOITEM` +- __Database Queries and Privilege__: + - `select, insert, update, delete` on `TODOOWNER.TODOITEM` + + +# Building the Application +The application uses Maven to build and manage the project with its dependencies. +Since the [Dockerfile](./src/main/docker/Dockerfile) expects the JAR, you need to run mvn first. +```bash +mvn clean package +``` + +When building for docker, you can use the following command: +```bash +docker build -f src/main/docker/Dockerfile -t . +``` + +# Deploying to Kubernetes +To deploy the application on Kubernetes, +the environment variables and image must be replaced. + +For example, you can create the following manifest.yaml file: +```yaml +# manifest +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend-deployment + labels: + app: backendapp +spec: + replicas: 1 + selector: + matchLabels: + app: backendapp + template: + metadata: + labels: + app: backendapp + spec: + containers: + - name: app + image: example:v1 # update with your container image + env: + - name: database.user + value: myUser # update with your database user + - name: database.url + value: myUrl # update with your database URL + + - name: database.password + valueFrom: + secretKeyRef: + name: myDatabasePWDSecret # update with your database secret + key: password + ports: + - containerPort: 8080 + restartPolicy: Always + +--- + +apiVersion: v1 +kind: Service +metadata: + name: backend-service +spec: + type: ClusterIP + ports: + - port: 8080 + targetPort: 8080 + selector: + app: backendapp +``` + +This configuration requires the following secret to be created: +```bash +kubectl create secret generic myDatabasePWDSecret --from-literal=password= +``` \ No newline at end of file From 41b487978173d2b5a905175973404c50a2e9e709 Mon Sep 17 00:00:00 2001 From: aberinnj Date: Fri, 6 Sep 2024 07:50:16 -0500 Subject: [PATCH 10/16] updated README --- mtdrworkshop/backend/README.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/mtdrworkshop/backend/README.md b/mtdrworkshop/backend/README.md index fc956dc..7a14aa7 100644 --- a/mtdrworkshop/backend/README.md +++ b/mtdrworkshop/backend/README.md @@ -80,8 +80,7 @@ spec: - name: database.user value: myUser # update with your database user - name: database.url - value: myUrl # update with your database URL - + value: "jdbc:oracle:thin:@
" # update with your database URL - name: database.password valueFrom: secretKeyRef: @@ -89,7 +88,22 @@ spec: key: password ports: - containerPort: 8080 + + # if database wallet is required + volumeMounts: + - name: creds + mountPath: /app/creds # update with the right path to the wallet + # end if + restartPolicy: Always + + # if database wallet is required + volumes: + - name: creds + secret: + secretName: db-wallet-secret # update with the actual secret + # end if + --- @@ -109,4 +123,9 @@ spec: This configuration requires the following secret to be created: ```bash kubectl create secret generic myDatabasePWDSecret --from-literal=password= +``` + +If a wallet is necessary, you can run the following command to create the wallet secret +```bash +kubectl create secret generic wallet --from-file= ``` \ No newline at end of file From c0fe59d10bd3477552e6d5c5159a43a4708b5372 Mon Sep 17 00:00:00 2001 From: aberinnj Date: Fri, 6 Sep 2024 07:51:06 -0500 Subject: [PATCH 11/16] Updated port --- mtdrworkshop/backend/src/main/k8s/manifest.yaml | 6 +++--- mtdrworkshop/ingress/ingress.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mtdrworkshop/backend/src/main/k8s/manifest.yaml b/mtdrworkshop/backend/src/main/k8s/manifest.yaml index c0475ca..bafe12a 100644 --- a/mtdrworkshop/backend/src/main/k8s/manifest.yaml +++ b/mtdrworkshop/backend/src/main/k8s/manifest.yaml @@ -1,4 +1,4 @@ -## MyToDoReact version 2.0.0 +## MyToDoReact version 2.1.0 ## ## Copyright (c) 2021 Oracle, Inc. ## Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ @@ -54,9 +54,9 @@ kind: Service metadata: name: backend-service spec: - type: NodePort + type: ClusterIP ports: - - port: 80 + - port: 8080 targetPort: 8080 selector: app: backendapp diff --git a/mtdrworkshop/ingress/ingress.yaml b/mtdrworkshop/ingress/ingress.yaml index dbfb617..3d5c6e0 100644 --- a/mtdrworkshop/ingress/ingress.yaml +++ b/mtdrworkshop/ingress/ingress.yaml @@ -14,7 +14,7 @@ spec: service: name: backend-service port: - number: 80 + number: 8080 - http: paths: - pathType: Prefix From f4a1faa141f9a288fd795d3b6702a56007a87f88 Mon Sep 17 00:00:00 2001 From: aberinnj Date: Fri, 6 Sep 2024 07:51:36 -0500 Subject: [PATCH 12/16] Updated scripts to reflect new table --- mtdrworkshop/utils/db-setup.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mtdrworkshop/utils/db-setup.sh b/mtdrworkshop/utils/db-setup.sh index 2b8bb4d..b330e25 100755 --- a/mtdrworkshop/utils/db-setup.sh +++ b/mtdrworkshop/utils/db-setup.sh @@ -1,7 +1,7 @@ #!/bin/bash -## MyToDoReact version 2.0.0 +## MyToDoReact version 2.1.0 ## -## Copyright (c) 2021 Oracle, Inc. +## Copyright (c) 2024 Oracle, Inc. ## Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ # Fail on error @@ -134,12 +134,17 @@ while ! state_done TODO_USER; do sqlplus /nolog < Date: Fri, 6 Sep 2024 12:39:36 -0500 Subject: [PATCH 13/16] Updated formatting and indentation --- mtdrworkshop/backend/sql/todoitem.sql | 13 +-- .../com/oracle/database/AppDatasource.java | 107 +++++++++--------- .../java/com/oracle/todoapp/TodoItem.java | 32 ++++-- .../com/oracle/todoapp/TodoItemStorage.java | 94 ++++++++------- 4 files changed, 129 insertions(+), 117 deletions(-) diff --git a/mtdrworkshop/backend/sql/todoitem.sql b/mtdrworkshop/backend/sql/todoitem.sql index eca8375..44d6345 100644 --- a/mtdrworkshop/backend/sql/todoitem.sql +++ b/mtdrworkshop/backend/sql/todoitem.sql @@ -1,10 +1,9 @@ - - CREATE TABLE TODOOWNER.TODOITEM ( - id NUMBER GENERATED ALWAYS AS IDENTITY, description VARCHAR2(4000), - creation_ts TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - done NUMBER(1,0) default 0, - PRIMARY KEY (id) + ID NUMBER GENERATED ALWAYS AS IDENTITY, + DESCRIPTION VARCHAR2(4000), + CREATION_TS TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + DONE NUMBER(1, 0) DEFAULT 0, + PRIMARY KEY (ID) ); -insert into TODOOWNER.todoitem (description) values ('My first task!'); \ No newline at end of file +INSERT INTO TODOOWNER.TODOITEM (DESCRIPTION) VALUES ('My first task!'); \ No newline at end of file diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/database/AppDatasource.java b/mtdrworkshop/backend/src/main/java/com/oracle/database/AppDatasource.java index 1197f4a..8b53354 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/database/AppDatasource.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/database/AppDatasource.java @@ -1,3 +1,9 @@ +/* +## MyToDoReact version 2.1.0 +## +## Copyright (c) 2024 Oracle, Inc. +## Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +*/ package com.oracle.database; import io.helidon.config.Config; @@ -10,63 +16,56 @@ public class AppDatasource { - // connection pooling using UCP - private final PoolDataSource pds; - private static AppDatasource instance = null; + // connection pooling using UCP + private final PoolDataSource pds; + private static AppDatasource instance = null; - // static get - public synchronized static AppDatasource get(Config c) { - if (instance == null) { - instance = new AppDatasource(c); - } - return instance; + // static get + public synchronized static AppDatasource get(Config c) { + if (instance == null) { + instance = new AppDatasource(c); } - - // constructor, initialize datasource - private AppDatasource(Config c) { - this.pds = PoolDataSourceFactory.getPoolDataSource(); - String url = c.get("url").asString().orElse(""); - String username = c.get("user").asString().orElse(""); - String password = c.get("password").asString().orElse(""); - - try { - - // In this application, we don't set any init, min or max size in UCP. We - // also don't start the pool explicitly. This means that the very first - // connection request will start the pool. The default maximum pool size - // is MAX_INT which isn't appropriate and should be configured properly in - // production. - this.pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); - this.pds.setConnectionPoolName("JDBC_UCP_POOL"); - this.pds.setInactiveConnectionTimeout(60); - this.pds.setMaxStatements(10); - - // if provided, set - if (!url.isEmpty()) { - this.pds.setURL(url); - } - if (!username.isEmpty()) { - this.pds.setUser(username); - } - if (!password.isEmpty()) { - this.pds.setPassword(password); - } - - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - - // retrieve connection - public Connection getConnection() throws SQLException { - return this.pds.getConnection(); + return instance; + } + + // constructor, initialize datasource + private AppDatasource(Config c) { + this.pds = PoolDataSourceFactory.getPoolDataSource(); + String url = c.get("url").asString().orElse(""); + String username = c.get("user").asString().orElse(""); + String password = c.get("password").asString().orElse(""); + + try { + + // In this application, we don't set any init, min or max size in UCP. We + // also don't start the pool explicitly. This means that the very first + // connection request will start the pool. The default maximum pool size + // is MAX_INT which isn't appropriate and should be configured properly in + // production. + this.pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + this.pds.setConnectionPoolName("JDBC_UCP_POOL"); + this.pds.setInactiveConnectionTimeout(60); + this.pds.setMaxStatements(10); + + // if provided, set + if (!url.isEmpty()) { + this.pds.setURL(url); + } + if (!username.isEmpty()) { + this.pds.setUser(username); + } + if (!password.isEmpty()) { + this.pds.setPassword(password); + } + + } catch (SQLException e) { + throw new RuntimeException(e); } + } - - - - - - + // retrieve connection + public Connection getConnection() throws SQLException { + return this.pds.getConnection(); + } } diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItem.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItem.java index 3dcb5de..96ab169 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItem.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItem.java @@ -12,7 +12,6 @@ import java.util.logging.Logger; import javax.json.Json; -import javax.json.JsonArray; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; @@ -21,7 +20,7 @@ * a description, a creation date and a boolean that indicates if the item is * done (in other words "complete") or not. * @author jean.de.lavarene@oracle.com - */ +*/ class TodoItem implements Serializable { private static final long serialVersionUID = 4168008245514009223L; @@ -32,7 +31,7 @@ class TodoItem implements Serializable { OffsetDateTime createdAt; boolean done; - static TodoItem of(int id, String description, OffsetDateTime when, boolean done){ + static TodoItem of(int id, String description, OffsetDateTime when, boolean done) { TodoItem todoItem = new TodoItem(); todoItem.setId(id); todoItem.setCreatedAt(when); @@ -40,19 +39,24 @@ static TodoItem of(int id, String description, OffsetDateTime when, boolean done todoItem.setDone(done); return todoItem; } + void setDescription(String description) { this.description = description; } - void setDone(boolean done){ + + void setDone(boolean done) { this.done = done; } + private void setCreatedAt(OffsetDateTime now) { this.createdAt = now; } + private void setId(int idStr) { this.id = idStr; } + int getId() { return this.id; } @@ -64,28 +68,31 @@ String getDescription() { OffsetDateTime getCreatedAt() { return createdAt; } - boolean isDone(){ + + boolean isDone() { return done; } + @Override public String toString() { - String ret= "TodoItem { \"id\": "+this.id - +", \"description\": "+this.description - +", \"createdAt\": "+this.createdAt - +", \"done\" : "+((done)?"true":"false") - +" }"; + String ret = "TodoItem { \"id\": " + this.id + + ", \"description\": " + this.description + + ", \"createdAt\": " + this.createdAt + + ", \"done\" : " + ((done) ? "true" : "false") + + " }"; return ret; } /** * Returns the Json representation of the given TodoItem instance. + * * @param item an object of TodoItem * @return */ static JsonObject toJsonObject(TodoItem item) { return Json.createObjectBuilder() .add("id", item.getId()) - .add("description", item.getDescription()==null?"":item.getDescription()) + .add("description", item.getDescription() == null ? "" : item.getDescription()) .add("createdAt", item.getCreatedAt().toString()) .add("done", item.isDone()) .build(); @@ -94,6 +101,7 @@ static JsonObject toJsonObject(TodoItem item) { /** * Returns an instance of TodoItem that corresponds to the given item in Json. + * * @param json a json object representing a todo item * @return an instance of this class */ @@ -114,6 +122,6 @@ static JsonObject toJsonArray(List items) { }); return Json.createObjectBuilder() - .add("items", itemsJSONArray).build(); + .add("items", itemsJSONArray).build(); } } diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java index 410d732..7338aae 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java @@ -45,19 +45,24 @@ class TodoItemStorage { TodoItemStorage(Config config) { LOGGER.log(Level.CONFIG, config::toString); ads = AppDatasource.get(config); + } /** - * We have two options to implement the backend. We can either make the database compute - * the JSON document or create the JSON document in Java. In this implementation we have - * chosen to build the JSON document in Java. So this method returns a list of TodoItem + * We have two options to implement the backend. We can either make the database + * compute + * the JSON document or create the JSON document in Java. In this implementation + * we have + * chosen to build the JSON document in Java. So this method returns a list of + * TodoItem * objects. * * The Oracle Database can also return the rows in a JSON format: * SELECT - * json_arrayagg(json_object('id' VALUE id, 'description' VALUE description, 'date' VALUE creation_ts)) + * json_arrayagg(json_object('id' VALUE id, 'description' VALUE description, + * 'date' VALUE creation_ts)) * FROM - * todoitem + * todoitem * ORDER BY creation_ts DESC * * @return a list of TodoItem objects @@ -66,48 +71,48 @@ List all() { LOGGER.fine("all"); ArrayList list = new ArrayList(); try ( - Connection conn = ads.getConnection(); - PreparedStatement pstmt = conn.prepareStatement("SELECT id, description, creation_ts, done FROM TODOOWNER.todoitem ORDER BY creation_ts DESC"); - ResultSet rs = pstmt.executeQuery(); - ) { - while(rs.next()) { + Connection conn = ads.getConnection(); + PreparedStatement pstmt = conn.prepareStatement( + "SELECT id, description, creation_ts, done FROM TODOOWNER.todoitem ORDER BY creation_ts DESC"); + ResultSet rs = pstmt.executeQuery();) { + while (rs.next()) { TodoItem item = TodoItem.of( - rs.getInt("id"), - rs.getString("description"), - rs.getObject("creation_ts", OffsetDateTime.class), - rs.getBoolean("done")); + rs.getInt("id"), + rs.getString("description"), + rs.getObject("creation_ts", OffsetDateTime.class), + rs.getBoolean("done")); list.add(item); } } catch (SQLException e) { - LOGGER.log(Level.SEVERE, e, ()->"in all()"); + LOGGER.log(Level.SEVERE, e, () -> "in all()"); } LOGGER.fine("all() returns:"); - LOGGER.fine(()->list.toString()); + LOGGER.fine(() -> list.toString()); return list; } TodoItem getById(int id) { - LOGGER.fine(()->"getById("+id+")"); + LOGGER.fine(() -> "getById(" + id + ")"); TodoItem ret = null; try ( Connection conn = ads.getConnection(); - PreparedStatement pstmt = conn.prepareStatement("SELECT id, description, creation_ts, done FROM todoowner.todoitem WHERE id=?"); - ){ + PreparedStatement pstmt = conn + .prepareStatement("SELECT id, description, creation_ts, done FROM todoowner.todoitem WHERE id=?");) { pstmt.setInt(1, id); try (ResultSet rs = pstmt.executeQuery();) { - if(rs.next()) { + if (rs.next()) { ret = TodoItem.of(rs.getInt("id"), rs.getString("description"), - rs.getObject("creation_ts", OffsetDateTime.class), rs.getBoolean("done")); + rs.getObject("creation_ts", OffsetDateTime.class), rs.getBoolean("done")); } } } catch (SQLException e) { - LOGGER.log(Level.SEVERE, e, ()->"in getById("+id+")"); + LOGGER.log(Level.SEVERE, e, () -> "in getById(" + id + ")"); } - if(ret != null){ - LOGGER.fine(()->"getById("+id+") returns:"); + if (ret != null) { + LOGGER.fine(() -> "getById(" + id + ") returns:"); LOGGER.fine(ret.toString()); } else { - LOGGER.fine(()->"getById("+id+") returns: null"); + LOGGER.fine(() -> "getById(" + id + ") returns: null"); } return ret; } @@ -117,35 +122,36 @@ TodoItem getById(String id) { int idInt = Integer.parseInt(id); return getById(idInt); } catch (NumberFormatException e) { - LOGGER.log(Level.INFO, ()->"NumberFormatException in getById("+id+"): "+e.getMessage()); + LOGGER.log(Level.INFO, () -> "NumberFormatException in getById(" + id + "): " + e.getMessage()); } return null; } + CompletionStage asyncSave(TodoItem item) { // Use 20c JDBC and UCP in order to use the async APIs return null; } + TodoItem save(TodoItem item) { - LOGGER.fine("save("+item.toString()+")"); + LOGGER.fine("save(" + item.toString() + ")"); int idInt = item.getId(); try ( - Connection conn = ads.getConnection(); - ){ - if(idInt >=0 ) { - try( - PreparedStatement pstmt = conn.prepareStatement("UPDATE todoowner.todoitem SET description=?, done=? WHERE id=?"); - ){ + Connection conn = ads.getConnection();) { + if (idInt >= 0) { + try ( + PreparedStatement pstmt = conn + .prepareStatement("UPDATE todoowner.todoitem SET description=?, done=? WHERE id=?");) { pstmt.setString(1, item.getDescription()); - pstmt.setInt(2, item.isDone()?1:0); + pstmt.setInt(2, item.isDone() ? 1 : 0); pstmt.setInt(3, idInt); int updateCount = pstmt.executeUpdate(); - LOGGER.info(()->"save - update case - updateCount="+updateCount); + LOGGER.info(() -> "save - update case - updateCount=" + updateCount); } return this.getById(idInt); } else { try ( - PreparedStatement pstmt = conn.prepareStatement("INSERT INTO todoowner.todoitem (description) VALUES (?)", new String[]{"id"}); - ){ + PreparedStatement pstmt = conn.prepareStatement("INSERT INTO todoowner.todoitem (description) VALUES (?)", + new String[] { "id" });) { pstmt.setString(1, item.getDescription()); int updateCount = pstmt.executeUpdate(); // this call blocks the thread @@ -154,12 +160,12 @@ TodoItem save(TodoItem item) { rs.next(); int id = rs.getInt(1); - LOGGER.fine(()->"save - insert case - updateCount="+updateCount); + LOGGER.fine(() -> "save - insert case - updateCount=" + updateCount); return this.getById(id); } } } catch (SQLException e) { - LOGGER.log(Level.SEVERE, e, ()->"in save(...)"); + LOGGER.log(Level.SEVERE, e, () -> "in save(...)"); } return this.getById(idInt); } @@ -169,21 +175,21 @@ boolean deleteById(String id) { int idInt = Integer.parseInt(id); return deleteById(idInt); } catch (NumberFormatException e) { - LOGGER.log(Level.INFO, ()->"NumberFormatException in deleteById("+id+"): "+e.getMessage()); + LOGGER.log(Level.INFO, () -> "NumberFormatException in deleteById(" + id + "): " + e.getMessage()); } return false; } + boolean deleteById(int id) { - LOGGER.fine(()->"deleteById(" + id + ")"); + LOGGER.fine(() -> "deleteById(" + id + ")"); int ret = 0; try ( Connection conn = ads.getConnection(); - PreparedStatement pstmt = conn.prepareStatement("DELETE FROM todoowner.todoitem WHERE id=?"); - ){ + PreparedStatement pstmt = conn.prepareStatement("DELETE FROM todoowner.todoitem WHERE id=?");) { pstmt.setInt(1, id); ret = pstmt.executeUpdate(); } catch (SQLException e) { - LOGGER.log(Level.SEVERE, e, ()->"in deleteById("+id+")"); + LOGGER.log(Level.SEVERE, e, () -> "in deleteById(" + id + ")"); } return (ret == 1); } From 9d39d701fbba880797df4048173cf73eae5f90cb Mon Sep 17 00:00:00 2001 From: aberinnj Date: Fri, 6 Sep 2024 12:47:23 -0500 Subject: [PATCH 14/16] Formatted file --- .../main/java/com/oracle/todoapp/Main.java | 54 +++++++++---------- .../oracle/todoapp/TodoListAppService.java | 44 ++++++++------- 2 files changed, 47 insertions(+), 51 deletions(-) diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java index a2644ea..7cd0773 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java @@ -18,8 +18,6 @@ import io.helidon.webserver.cors.CorsSupport; import io.helidon.webserver.cors.CrossOriginConfig; - - /* * This is the helidon-se backend. * @author jean.de.lavarene@oracle.com @@ -36,24 +34,23 @@ public static void main(final String[] args) .readConfiguration( Main.class.getResourceAsStream("/logging.properties")); Config config = Config.create(); - - WebServer.builder() - .config(config.get("server")) //update this server configuration from the config provided - .addMediaSupport(JsonpSupport.create()) - .routing(createRouting(config)) - .build() - .start() - .thenAccept(ws -> { - System.out.printf( - "webserver is up! http://localhost:%s/api/todolist%n", ws.port()); - ws.whenShutdown().thenRun(() -> - System.out.println("WEB server is DOWN. Good bye!")); - }) - .exceptionally(t -> { - System.err.printf("Startup failed: %s%n", t.getMessage()); - t.printStackTrace(System.err); - return null; - }); + + WebServer.builder() + .config(config.get("server")) // update this server configuration from the config provided + .addMediaSupport(JsonpSupport.create()) + .routing(createRouting(config)) + .build() + .start() + .thenAccept(ws -> { + System.out.printf( + "webserver is up! http://localhost:%s/api/todolist%n", ws.port()); + ws.whenShutdown().thenRun(() -> System.out.println("WEB server is DOWN. Good bye!")); + }) + .exceptionally(t -> { + System.err.printf("Startup failed: %s%n", t.getMessage()); + t.printStackTrace(System.err); + return null; + }); } private static Routing createRouting(Config config) { @@ -64,15 +61,15 @@ private static Routing createRouting(Config config) { TodoListAppService todoListAppService = new TodoListAppService(config.get("database")); // Enables CORS - // update if needed - CorsSupport corsSupport = CorsSupport.builder() + // update if needed + CorsSupport corsSupport = CorsSupport.builder() .addCrossOrigin(CrossOriginConfig.builder() .allowOrigins() .allowMethods("POST", "PUT", "DELETE") .exposeHeaders("location", "timestamp") .build()) .addCrossOrigin(CrossOriginConfig.create()) - .build(); + .build(); // Create routing and register return Routing @@ -81,13 +78,14 @@ private static Routing createRouting(Config config) { .error(Throwable.class, handleErrors()) .build(); } + private static ErrorHandler handleErrors() { return (req, res, t) -> { - if (t instanceof TodoItemNotFoundException) { - res.status(404).send(t.getMessage()); - } else { - req.next(t); - } + if (t instanceof TodoItemNotFoundException) { + res.status(404).send(t.getMessage()); + } else { + req.next(t); + } }; } } diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoListAppService.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoListAppService.java index 7ac65da..da066bc 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoListAppService.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoListAppService.java @@ -35,11 +35,11 @@ public TodoListAppService(Config config) { @Override public void update(Rules rules) { rules - .get("/", this::getAllTodos) - .post("/", this::saveTodo) - .get("/{id}", this::getTodoById) - .put("/{id}", this::updateTodo) - .delete("/{id}", this::deleteTodoById); + .get("/", this::getAllTodos) + .post("/", this::saveTodo) + .get("/{id}", this::getTodoById) + .put("/{id}", this::updateTodo) + .delete("/{id}", this::deleteTodoById); } private void getAllTodos(ServerRequest serverRequest, ServerResponse serverResponse) { @@ -50,25 +50,22 @@ private void getAllTodos(ServerRequest serverRequest, ServerResponse serverRespo private void saveTodo(ServerRequest serverRequest, ServerResponse serverResponse) { LOGGER.fine("saveTodo"); serverRequest.content().as(JsonObject.class) - .thenApply( - /* convert from JSON to TodoItem object */ - TodoItem::fromJsonObject) - .thenApply(todoItem -> - TodoItem.of(todoItem.getId(),todoItem.getDescription(), - todoItem.getCreatedAt(),todoItem.isDone()) - ) - .thenApply(this.todoItems::save) // this blocking + .thenApply( + /* convert from JSON to TodoItem object */ + TodoItem::fromJsonObject) + .thenApply(todoItem -> TodoItem.of(todoItem.getId(), todoItem.getDescription(), + todoItem.getCreatedAt(), todoItem.isDone())) + .thenApply(this.todoItems::save) // this blocking - // Use async APIs when avaiable: - // .thenCompose(this.todoItems::asyncSave) - .thenCompose( - todoItem -> { + // Use async APIs when avaiable: + // .thenCompose(this.todoItems::asyncSave) + .thenCompose( + todoItem -> { serverResponse.status(201) .headers().add("timestamp", todoItem.getCreatedAt().toString()) - .location(URI.create(""+todoItem.getId())); + .location(URI.create("" + todoItem.getId())); return serverResponse.send(); - } - ); + }); } @@ -77,12 +74,13 @@ private void getTodoById(ServerRequest serverRequest, ServerResponse serverRespo String id = serverRequest.path().param("id"); TodoItem todo = this.todoItems.getById(id); if (todo == null) { - // todo identified by "id" wasn't found: + // todo identified by "id" wasn't found: serverRequest.next(new TodoItemNotFoundException(id)); } else { serverResponse.status(200).send(TodoItem.toJsonObject(todo)); } } + private void updateTodo(ServerRequest serverRequest, ServerResponse serverResponse) { LOGGER.fine("updateTodo"); TodoItem item = this.todoItems.getById(serverRequest.path().param("id")); @@ -97,9 +95,9 @@ private void updateTodo(ServerRequest serverRequest, ServerResponse serverRespon // Use async APIs when avaiable: // .thenCompose(this.todiItems::asyncSave) .thenCompose( - p -> serverResponse.status(200).send(TodoItem.toJsonObject(p)) - ); + p -> serverResponse.status(200).send(TodoItem.toJsonObject(p))); } + void deleteTodoById(ServerRequest serverRequest, ServerResponse serverResponse) { LOGGER.fine("deleteTodo"); this.todoItems.deleteById(serverRequest.path().param("id")); From 832e3dc1d50c03ceba63241266104f3f464cf75d Mon Sep 17 00:00:00 2001 From: aberinnj Date: Fri, 6 Sep 2024 12:58:14 -0500 Subject: [PATCH 15/16] Updated indentation --- mtdrworkshop/backend/build.sh | 2 +- mtdrworkshop/backend/deploy.sh | 4 ++-- mtdrworkshop/backend/set.sh | 20 +++++++++---------- .../com/oracle/todoapp/TodoItemStorage.java | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/mtdrworkshop/backend/build.sh b/mtdrworkshop/backend/build.sh index c836bae..5a6c4c9 100755 --- a/mtdrworkshop/backend/build.sh +++ b/mtdrworkshop/backend/build.sh @@ -13,6 +13,6 @@ mvn clean package docker build -f src/main/docker/Dockerfile -t $IMAGE . if [ $? -ne 0 ]; then - exit 1 + exit 1 fi docker push $IMAGE diff --git a/mtdrworkshop/backend/deploy.sh b/mtdrworkshop/backend/deploy.sh index 932e5b8..fe7fd8c 100755 --- a/mtdrworkshop/backend/deploy.sh +++ b/mtdrworkshop/backend/deploy.sh @@ -32,9 +32,9 @@ mv -- /tmp/"$YAML_NAME" "$YAML_NAME" if [ -z "$1" ]; then - kubectl apply -f $SCRIPT_DIR/"$YAML_NAME" -n mtdrworkshop + kubectl apply -f $SCRIPT_DIR/"$YAML_NAME" -n mtdrworkshop else - kubectl apply -f <(istioctl kube-inject -f $SCRIPT_DIR/"$YAML_NAME") -n mtdrworkshop + kubectl apply -f <(istioctl kube-inject -f $SCRIPT_DIR/"$YAML_NAME") -n mtdrworkshop fi #kubectl apply -f $SCRIPT_DIR/order-service.yaml -n mtdrworkshop diff --git a/mtdrworkshop/backend/set.sh b/mtdrworkshop/backend/set.sh index e4cf4cb..56bde9f 100755 --- a/mtdrworkshop/backend/set.sh +++ b/mtdrworkshop/backend/set.sh @@ -8,28 +8,28 @@ export IMAGE_NAME=todolistapp-helidon-se export IMAGE_VERSION=0.1 if [ -z "$DOCKER_REGISTRY" ]; then - echo "DOCKER_REGISTRY not set. Will get it with state_get" + echo "DOCKER_REGISTRY not set. Will get it with state_get" export DOCKER_REGISTRY=$(state_get DOCKER_REGISTRY) fi if [ -z "$DOCKER_REGISTRY" ]; then - echo "Error: DOCKER_REGISTRY env variable needs to be set!" - exit 1 + echo "Error: DOCKER_REGISTRY env variable needs to be set!" + exit 1 fi if [ -z "$TODO_PDB_NAME" ]; then - echo "TODO_PDB_NAME not set. Will get it with state_get" + echo "TODO_PDB_NAME not set. Will get it with state_get" export TODO_PDB_NAME=$(state_get MTDR_DB_NAME) fi if [ -z "$TODO_PDB_NAME" ]; then - echo "Error: TODO_PDB_NAME env variable needs to be set!" - exit 1 + echo "Error: TODO_PDB_NAME env variable needs to be set!" + exit 1 fi if [ -z "$OCI_REGION" ]; then - echo "OCI_REGION not set. Will get it with state_get" - export OCI_REGION=$(state_get REGION) + echo "OCI_REGION not set. Will get it with state_get" + export OCI_REGION=$(state_get REGION) fi if [ -z "$OCI_REGION" ]; then - echo "Error: OCI_REGION env variable needs to be set!" - exit 1 + echo "Error: OCI_REGION env variable needs to be set!" + exit 1 fi export IMAGE=${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_VERSION} diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java index 7338aae..2df56bf 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItemStorage.java @@ -74,7 +74,7 @@ List all() { Connection conn = ads.getConnection(); PreparedStatement pstmt = conn.prepareStatement( "SELECT id, description, creation_ts, done FROM TODOOWNER.todoitem ORDER BY creation_ts DESC"); - ResultSet rs = pstmt.executeQuery();) { + ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { TodoItem item = TodoItem.of( rs.getInt("id"), @@ -99,7 +99,7 @@ TodoItem getById(int id) { PreparedStatement pstmt = conn .prepareStatement("SELECT id, description, creation_ts, done FROM todoowner.todoitem WHERE id=?");) { pstmt.setInt(1, id); - try (ResultSet rs = pstmt.executeQuery();) { + try (ResultSet rs = pstmt.executeQuery()) { if (rs.next()) { ret = TodoItem.of(rs.getInt("id"), rs.getString("description"), rs.getObject("creation_ts", OffsetDateTime.class), rs.getBoolean("done")); From feb456a0d324474c46a49be87f8002e5f0ea809d Mon Sep 17 00:00:00 2001 From: aberinnj Date: Fri, 6 Sep 2024 13:02:16 -0500 Subject: [PATCH 16/16] Updated Java files --- .../backend/src/main/java/com/oracle/todoapp/Main.java | 6 ++---- .../backend/src/main/java/com/oracle/todoapp/TodoItem.java | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java index 7cd0773..f5a3cf6 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/Main.java @@ -7,7 +7,6 @@ package com.oracle.todoapp; import java.io.IOException; -import java.sql.SQLException; import java.util.logging.LogManager; import io.helidon.config.Config; @@ -24,8 +23,7 @@ */ public final class Main { - public static void main(final String[] args) - throws IOException, SQLException { + public static void main(final String[] args) throws IOException { System.out.println("Working Directory = " + System.getProperty("user.dir")); System.setProperty("oracle.jdbc.fanEnabled", "false"); @@ -34,7 +32,7 @@ public static void main(final String[] args) .readConfiguration( Main.class.getResourceAsStream("/logging.properties")); Config config = Config.create(); - + WebServer.builder() .config(config.get("server")) // update this server configuration from the config provided .addMediaSupport(JsonpSupport.create()) diff --git a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItem.java b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItem.java index 96ab169..506de1e 100644 --- a/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItem.java +++ b/mtdrworkshop/backend/src/main/java/com/oracle/todoapp/TodoItem.java @@ -121,7 +121,6 @@ static JsonObject toJsonArray(List items) { itemsJSONArray.add(toJsonObject(p)); }); - return Json.createObjectBuilder() - .add("items", itemsJSONArray).build(); + return Json.createObjectBuilder().add("items", itemsJSONArray).build(); } }