Note:
- This tutorial requires access to Oracle Cloud. To sign up for a free account, see Get started with Oracle Cloud Infrastructure Free Tier.
- It uses example values for Oracle Cloud Infrastructure credentials, tenancy, and compartments. When completing your lab, substitute these values with ones specific to your cloud environment.
Create an Oracle GraalVM based Java Application using Spring Boot on OKE to Store SOAP Messages in ATP and Send to OCI Queue
Introduction
Many of our customers rely on traditional Simple Object Access Protocol (SOAP) messaging to enable communication between their applications. They often need to store transactional data, ensure service decoupling, achieve incoming requests load balancing, and enable asynchronous communication of these messages.
In this tutorial, we will see how we can build an Oracle GraalVM based Java application using Spring Boot deployed on the Oracle Cloud Infrastructure Kubernetes Engine (OKE) infrastructure that will be used as transactional integrator with the different Oracle Cloud Infrastructure (OCI) services such as Oracle Autonomous Transaction Processing (ATP) and OCI Queue. This setup allows systems to interact without direct connections, facilitating communication between applications with different processing speeds. Additionally, once the information is stored in the database, it can be consulted or analyzed.
We will use the following technologies:
Oracle Cloud Infrastructure Services (OCI): OCI is a secure, high-performance cloud platform offering more than 150-plus cloud services. It is designed for scalability, security, and performance.
-
Oracle Cloud Infrastructure Kubernetes Engine (OKE): OKE is a managed service for deploying, managing, and scaling containerized applications on OCI. It automates tasks like provisioning, scaling, and monitoring while ensuring robust security and OCI integration. OKE supports both stateless and stateful workloads, offering a flexible platform for modern app development.
-
OCI Container Registry Classic: It is a managed Docker-compatible registry in OCI that allows users to store, manage, and deploy container images securely. It integrates seamlessly with OKE and other OCI services, enabling efficient containerized application deployment.
-
Oracle Autonomous Transaction Processing (ATP): ATP is a cloud database service that automates tasks like provisioning, patching, scaling, and backup, ensuring high availability and security. It delivers powerful performance for transaction-oriented workloads, leveraging machine learning for autonomous operations and performance optimization. With features like application development made easy, generative AI, complete data protection, and security, ATP ensures constant high performance and maximum availability.
-
OCI Queue: It is a fully managed, serverless message queue service that enables asynchronous, decoupled communication between distributed applications. It ensures reliable message delivery with features like automatic scaling, message persistence, and fair processing. OCI Queue integrates with other OCI services and supports industry-standard protocols, ideal for scalable, event-driven architectures.
Oracle Technology:
- Oracle GraalVM It is a high-performance, polyglot virtual machine (VM) that supports multiple programming languages like Java, JavaScript, Python, and more. It improves performance with Just in Time (JIT) and Ahead of Time (AOT) compilation, enabling faster startup and lower resource usage. Oracle GraalVM also offers tools for optimization and profiling, making it ideal for microservices and cloud-native applications.
Other Technologies:
- Spring Boot: It is a Java framework that simplifies creating stand-alone, production-ready applications with auto-configuration and embedded web servers. It offers built-in features like health checks and metrics, making it ideal for quickly building microservices and scalable applications.
OCI High Level Architecture:
Note:
- It is important to clarify that JMeter will simulate the SOAP messages produced by customer applications.
- This tutorial is intended for educational purposes, offering students a controlled environment to explore and gain practical experience. Note that the security configurations and practices demonstrated are designed for learning and may not be appropriate for production environments or real-world applications.
Objectives
-
Build Oracle GraalVM based Java application using Spring Boot framework and provision the entire OCI services required to store transactional messages in ATP and send them to OCI Queue.
-
Provision and configure OKE cluster.
-
Provision, configure and access to OCI Container Registry Classic.
-
Provision and configure ATP serverless database.
-
Connect and create project tables in ATP.
-
Provision and configure OCI Queue.
-
Build Oracle GraalVM based Java application with Spring Boot and deploy in OKE.
-
Test Spring Boot Oracle GraalVM application using JMeter.
-
Prerequisites
-
OCI Environment: It provides the necessary cloud infrastructure, services, and security configurations to deploy, manage, and scale applications effectively in this tutorial.
-
Access to an OCI tenant. To create a free Oracle Cloud account, see Create a Free Oracle Cloud Account.
-
Create VCN with public and private subnets. For more information, see Creating a VCN and Creating a Subnet.
-
Create a compartment where you can group your OCI services. For more information, see Creating a Compartment.
-
-
Administration Host: It is important to have an administration host (In this tutorial, we used Oracle Linux 8) as shown in the high level architecture. The admin host must be configured with OCI CLI, kubectl and Docker to be able to monitor, have visibility and control of the OKE cluster and OCI Container Registry Classic.
-
Provisioning administration host. For more information, see Creating an Instance.
-
Install Oracle Cloud Infrastructure Command Line Interface (OCI CLI). For more information, see Installing OCI CLI.
-
Install Docker on Oracle Linux 8/7. For more information, see Installing Docker.
-
Install and set up kubectl on Linux. For more information, see Installing kubectl.
-
-
Development Environment: We need a development environment to write, test, and debug code. Install Apache Maven and Oracle GraalVM.
-
Install Oracle GraalVM. For more information, see Getting Started with Oracle GraalVM.
-
Download and install Apache Maven. For more information, see Download Maven and Install Maven.
-
Develop Spring Boot application. For more information, see Developing Your First Spring Boot Application and Spring Quickstart Guide.
-
Task 1: Provision and Configure OKE Cluster
In this task, we will provision the Kubernetes platform, where the application will support all the SOAP high transactionality messages to store in ATP and send each one to OCI Queue in real time.
-
Log in to the OCI Console, navigate to Developer Services, Kubernetes Clusters (OKE), and select Compartment of your preference.
There are two ways to create an OKE cluster:
- Quick Create.
- Custom Create.
-
Select Quick create as this method is easier, faster and automatically deploys all the elements required by OKE for its operation, such as:
- Virtual Cloud Network (VCN).
- Internet Gateway.
- Network Address Translation (NAT) gateway.
- Service gateway.
- Kubernetes cluster.
- Kubernetes worker nodes(s) and node pool.
Note: For the enterprise environments, where already the customers have services, network, infrastructure, it is important customize the OKE deployment, to be in compliance, aligned with the client architecture, resources and following the best practices.
-
Click Create cluster and enter the following information.
- Name: Enter the name of OKE cluster.
- Compartment: Select the compartment created for this project.
- Kubernetes version: Select the latest Kubernetes version available.
- Kubernetes API endpoint: In this tutorial, select Public endpoint, but you can also select Private endpoint.
- Node Type: Select Managed nodes.
- Kubernetes worker nodes: Select Private workers.
- Shape and image: Select VM.Standard.E5.Flex, customize the number of OCPUs (2) and memory (16GB) and conserve default Oracle Linux 8 image.
- Node Count: Enter 2 worker nodes to be deployed with OKE node pool.
Review that the OKE cluster is working.
Task 2: Provision, Configure and Access to OCI Container Registry Classic
We need to manage the project images in a repository. To achieve this, we will provision the OCI Container Registry Classic. Once the images are stored in the OCI Container Registry Classic, we will be able to deploy them in OKE.
-
Go to the OCI Console, navigate to Developer Services, Containers & Artifacts, Container Registry and click Create Repository.
-
Enter the following information and click Create.
- Create in Compartment: Select the compartment created for this project.
- Access: Select Public.
- Repository name: Enter
springboot/tutorialapp
.
-
Once the repository is created, from your Oracle admin host access it using the following command.
docker login -u 'tenancy_namespace/domain/username' regionID.ocir.io
Password: xxxxxx
Task 3: Provision and Configure Oracle Autonomous Transaction Processing (ATP) Serverless Database
In ATP database, we will store the data of each SOAP message received per transaction, approximately each one will be in the order of milliseconds, attending to parallel and sequential insertions.
-
Go to the OCI Console, navigate to Oracle Database and click Autonomous Transaction Processing.
-
Click Create Autonomous Database and enter the following information.
- Select the Compartment: Select the compartment created for this project.
- Display Name: Enter the display name.
- Display Database Name: Enter the database name.
- Choose a workload type: Select Transaction Processing.
- Choose a deployment type: Select Serverless.
- Configure the database:
- Developer: Deselect it.
- Choose database version: Select 23ai.
- ECPU count: Enter 2.
- Compute auto scaling: Select it.
- Storage: Enter 1024GB.
- Automatic backup retention period in days: Leave the default option 60 days.
- Create administrator credentials:
- Username: Is
ADMIN
by default and it cannot be edited. - Password: Enter your preferred password.
- Confirm password: Enter the password again.
- Username: Is
- Choose network access: Select Private endpoint access only then choose the VCN and subnet created for this project. This setting restricts connections to the specified private network (VCN) only. However, you can choose other options, this depends on the needs of the company.
Review that the ATP database is running.
Task 4: Connect and Create Project Tables in Oracle Autonomous Transaction Processing (ATP)
Now, we need to configure, connect and create the project tables in the ATP database that was generated in Task 3.
-
Go to the OCI Console, navigate to Oracle Database, Autonomous Transaction Processing and click Database Connection. Select TLS authentication, TLS and click Download Wallet.
-
Unzip the wallet
.zip
file and intnsnames.ora
, you can get the datasource URL to get the connection to this database. Save this datasource URL.For example:
tutorialoracleatp_medium = (description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1522)(host=xxxxxxxx.adb.sa-saopaulo-1.oraclecloud.com))(connect_data=(service_name=xxxxxxxxxxxxxxx_tutorialoracleatp_medium.adb.oraclecloud.com))(security=(ssl_server_dn_match=no)))
-
Access to the database is now necessary. When the ATP was provisioned, Oracle REST Data Services (ORDS) access was enabled. For more information, see Oracle REST Data Services.
Go to the Autonomous Database details page, click Database actions. Note that it can only accessed from a compute instance running in the same virtual cloud network (VCN).
-
Paste the URL in the browser and access to ORDS using the user and password entered in the ATP database previously and access the Oracle SQL Developer Web module.
-
Use the following queries to create the tables
USERS
,CARS
andHOUSES
related with the SOAP messages that we will receive.CREATE TABLE USERS ( username varchar(50) NOT NULL, userlastname varchar(50) NOT NULL, id int NOT NULL, email varchar(50) NOT NULL, dateuser varchar(50) NOT NULL, attributeuser varchar(50) NOT NULL ); CREATE TABLE CARS ( userid int NOT NULL, brand varchar(50) NOT NULL, color varchar(50) NOT NULL, plate varchar(50) NOT NULL ); CREATE TABLE HOUSES ( userid int NOT NULL, floors int NOT NULL, locationhouse varchar(50) NOT NULL, rooms int NOT NULL, bathrooms int NOT NULL );
Task 5: Provision and Configure OCI Queue
In the OCI Queue, we will store highly transactional messages through RESTful HTTP API for a specific period of time. Consumers can then read and delete the messages immediately or at their convenience, ensuring decoupling and preventing data loss.
-
Go to the OCI Console, navigate to Developer Services, Application Integration and click Queues.
-
Click Create queue, enter the following information and click Create queue.
- Name: Enter the name adequate for your queue.
- Compartment: Select your work compartment.
- Queue settings: In this tutorial, we will select Default configuration, but if you prefer, you can customize multiple options according to business needs such as: Visibility timeout, Maximum retention period, Maximum channel consumption and Dead letter queue settings.
- Configure encryption settings: Select Oracle-managed key, but Customer-managed key is also an option.
Review that the OCI Queue is running.
Task 6: Build Oracle GraalVM based Java application with Spring Boot and Deploy it in OKE
Now, we will develop and deploy an Oracle GraalVM based Java application on Spring Boot that will perform the following tasks:
-
Get the data from the XML SOAP messages received in each HTTP Post request.
-
Insert the data extracted from XML SOAP message per transaction in ATP.
-
Convert from SOAP XML format to JSON format.
-
Put in OCI Queue each message converted.
Note: Before starting, it is important to have an administration host and development environment created as shown in Prerequisites - Administration Host and Prerequisites - Development Environment section.
Once your admin host and development environment are configured and are ready, you can start to develop your Spring Boot project.
-
Go to Spring initializr and create first project that will give us the folders structure and base files of a Spring Boot project, to later modify it according to our requirements. Enter the following information and click Generate, this will automatically download the Spring Boot project, save and unzip it in your development host.
- Project: Select Maven.
- Language: Select Java.
- Spring boot: Select 3.3.6.
- Project Metadata:
- Group: Enter
com.tutorial_springboot
. - Artifact: Enter tutorial.
- Name: Enter tutorial.
- Description: Enter Spring Boot Application (read SOAP, transform to JSON, insert ATP and Put in OCI Queue).
- Group: Enter
- Packaging: Select Jar.
- Java: Select 17.
- Dependencies: Select Oracle Driver, Spring Web Services and Spring Web.
Note: We can add some dependencies in the project, and later add more, directly in the
pom.xml
file, according our needs. -
Now we have the spring boot structure project.
Review the
pom.xml
file, we will start to work on it.Update the pom.xml file, according to the scope proposed in this tutorial.
-
Add the
oci sdk
version and the following dependencies.oci-java-sdk-common
.oci-java-sdk-queue
.oci-java-sdk-addons-oke-workload-identity
.oci-java-sdk-common-httpclient-jersey3
.
The application will need to authenticate with OCI, connect and manage OCI services, such as OCI Queue.
-
Already our
pom.xml
file hasspring-boot-starter-web-services
dependency and we have to addwsdl4j
dependency. The main purpose is to get the data received from SOAP messages and put it in Java objects, creating spring web services manipulating XML payloads, aims to facilitate contract-first SOAP service development. Also allows configure port, URI and set XML schema loaded from XML Schema Definition (XSD) file. -
Add JSON dependency. This library will be used to generate JSON format with the data extracted from SOAP message.
-
Add
spring-boot-maven-plugin
plugin in Build section. This plugin will allow us to generate the jar executable Spring Boot project file. -
Add
jaxb2-maven-plugin
plugin in Build section. This plugin will use Java API for XML binding (JAXB), to generate Java classes from XML schemas and in this way, we can pass the data from the SOAP message to Java class objects created by us.In this plugin section, it is important to put the configuration that will indicate the path, where the XSD file is included in our Spring Boot project.
<configuration> <sources> <source>${project.basedir}/src/main/resources/messages.xsd<source> </sources> </configuration>
-
Add
jasypt-spring-boot-starter
dependency in Dependencies section andjasypt-maven-plugin
plugin in Build section that will allow us to encrypt sensitive parameters in theapplication.properties
file, ensuring secure usage within our application.
Review the following dependencies added in the
pom.xml
file. -
-
Download the libraries and run the following commands.
-
In your development environment, run the following command to access your project.
cd tutorial
-
Clean the project and remove all files generated by the previous build.
mvn clean
-
Purge (delete and optionally re-resolve) artifacts from the local maven repository.
mvn dependency:purge-local-repository
-
-
Already we have the dependencies and the
pom.xml
file configured in our project, we will proceed to check the SOAP XML file, as it represents the request from the client’s side, and the XSD file, which interprets the request on our Spring Boot project side.-
This SOAP XML file has two messages, with personal information and other kinds of attributes from two different clients that we will send per request as shown in the following image.
-
Now on our Spring Boot project side, an XML schema is necessary to define a web service domain that Spring web service automatically exports as a WSDL, the following image shows the
messages.xsd
file defined for this tutorial.messages.xsd:
-
Save the
messages.xsd
file in the resources folder of your Spring Boot project.
-
-
Build and install project files in a jar file. Run the following command and ensure that you are in your Spring Boot project folder.
mvn install
Note: Once the maven install command is executed, the target folder is automatically generated, and in the same way within it, the Java classes according to XSD file created previously and the project’s executable
.jar
file. -
Now, we can add the required Java classes to our Spring Boot project.
WebServiceConfig.java Class:
This Java class was developed to create SOAP web service:- Sets up servlet to handle SOAP requests.
- Generates a WSDL definition based on an XML schema.
- Defines access endpoint of the SOAP web service.
- Uses the
messages.xsd
schema file from the classpath.
//Imports import org.springframework.boot.web.servlet.ServletRegistrationBean; //import the ServletRegistrationBean class import org.springframework.context.ApplicationContext; //import the ApplicationContext class import org.springframework.context.annotation.Bean; //import the Bean class import org.springframework.context.annotation.Configuration; //import the Configuration class import org.springframework.core.io.ClassPathResource; //import the ClassPathResource class import org.springframework.ws.config.annotation.EnableWs; //import the EnableWs class import org.springframework.ws.config.annotation.WsConfigurerAdapter; //import the WsConfigurerAdapter class import org.springframework.ws.transport.http.MessageDispatcherServlet; //import the MessageDispatcherServlet class import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition; //import the DefaultWsdl11Definition class import org.springframework.xml.xsd.SimpleXsdSchema; //import the SimpleXsdSchema class import org.springframework.xml.xsd.XsdSchema; //import the XsdSchema class //Configuration class for the Web Service configuration @EnableWs //Enable the Web Service @Configuration //Define the class as a Configuration class public class WebServiceConfig extends WsConfigurerAdapter { //Create a ServletRegistrationBean object to register the MessageDispatcherServlet object with the application context @Bean public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) { MessageDispatcherServlet servlet = new MessageDispatcherServlet(); //Create a MessageDispatcherServlet object servlet.setApplicationContext(applicationContext); //Set the application context for the MessageDispatcherServlet object servlet.setTransformWsdlLocations(true); //Set the transformWsdlLocations property to true return new ServletRegistrationBean<>(servlet, "/ws/*"); //Return a new ServletRegistrationBean object with the MessageDispatcherServlet object and the URL pattern } //Create a DefaultWsdl11Definition object to define the WSDL @Bean(name = "messages") public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema messagesSchema) { DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition(); //Create a DefaultWsdl11Definition object wsdl11Definition.setPortTypeName("MessagesPort"); //Set the port type name wsdl11Definition.setLocationUri("/ws"); //Set the location URI wsdl11Definition.setTargetNamespace("http://tutorial_example.com/ns0"); //Set the target namespace wsdl11Definition.setSchema(messagesSchema); //Set the schema return wsdl11Definition; //Return the DefaultWsdl11Definition object } //Create a XsdSchema object to define the schema @Bean public XsdSchema messagesSchema() { return new SimpleXsdSchema(new ClassPathResource("messages.xsd")); //Return a new SimpleXsdSchema object with the messages.xsd file } }
Note: If you want to test the web service, you can run the Spring Boot project in the same local development environment desktop and send HTTP request using
curl
, as follows:mvn spring-boot:run
Once the project is running and the web service is up, run a local SOAP HTTP request using
curl
, as follows:curl --location 'http://localhost:8080/ws/'
And you will get a response from the web service exposed in our Spring Boot project.
-
Create a folder called
model
and in this folder we will add the following Java classes.Note: These Java classes
Car
,House
andUser
will retrieve the information based on the data in each SOAP message extracted from the HTTP SOAP request.-
Car.java class:
This Java class represents a Car object with their attributes linked to each user.``` //Imports import org.springframework.stereotype.Component; // For component scanning import org.springframework.context.annotation.Scope; // For defining bean scope @Component // Marks a class as a Spring-managed component @Scope("prototype") //A new instance is created every time the bean is requested public class Car { //Attributes private String brand; private String color; private String plate; //"getter" and "setter" methods to get and set the information in each object public String getBrand(){ return brand; } public void setBrand(String brand){ this.brand = brand; } public String getColor(){ return color; } public void setColor(String color){ this.color = color; } public String getPlate(){ return plate; } public void setPlate(String plate){ this.plate = plate; } } ```
-
House.java class:
This Java class represents a House object with their attributes linked to each user.//Imports import org.springframework.stereotype.Component; // For component scanning import org.springframework.context.annotation.Scope; // For defining bean scope @Component // Marks a class as a Spring-managed component @Scope("prototype") //A new instance is created every time the bean is requested public class House { //Attributes private int floors; private String location; private int rooms; private int bathrooms; //"getter" and "setter" methods to get and set the information in each object public int getFloors(){ return floors; } public void setFloors(int floors){ this.floors = floors; } public String getLocation(){ return location; } public void setLocation(String location){ this.location = location; } public int getRooms(){ return rooms; } public void setRooms(int rooms){ this.rooms = rooms; } public int getBathRooms(){ return bathrooms; } public void setBathRooms(int bathrooms){ this.bathrooms = bathrooms; } }
-
User.java class:
This Java class represents a User object with their attributes, containing Car and House objects.//Imports import org.springframework.stereotype.Component; // For component scanning import org.springframework.context.annotation.Scope; // For defining bean scope @Component // Marks a class as a Spring-managed component @Scope("prototype") //A new instance is created every time the bean is requested public class User { //Attributes private String username; private String userlastname; private int id; private String email; private String date; private String attribute; private Car car; private House house; //"getter" and "setter" methods to get and set the information in each object public String getUserName(){ return username; } public void setUserName(String username){ this.username = username; } public String getUserLastName(){ return userlastname; } public void setUserLastName(String userlastname){ this.userlastname = userlastname; } public int getID(){ return id; } public void setID(int id){ this.id = id; } public String getEmail(){ return email; } public void setEmail(String email){ this.email = email; } public String getDate(){ return date; } public void setDate(String date){ this.date = date; } public String getAttribute(){ return attribute; } public void setAttribute(String attribute){ this.attribute = attribute; } public Car getCar(){ return car; } public void setCar(Car car){ this.car = car; } public House getHouse(){ return house; } public void setHouse(House house){ this.house = house; } }
-
-
-
Now we will configure the necessary parameters in our Spring Boot project to store the obtained data in the OCI ATP database. In the
resources
folder, you must find theapplication.properties
file used to add parameters required by the application. It is automatically generated and is loaded by Spring Boot when the application starts.Note: It is really important to manage encryption and security methods that ensure sensitive information, such as passwords or relevant data, cannot be extracted or viewed by hackers. In this tutorial, we use the
jasypt
library configured inpom.xml
file. For more information, see How to encrypt passwords in a Spring Boot project using Jasypt. Additionally in our Java classes on Spring Boot it is documented where to add the annotations and source code related with this library to decrypt theapplication.properties
parameters.Add the appropriate parameters required by ATP database in the
application.properties
file, as shown in the following image.Create the Spring Boot Java class to store each message in the database, it will be in
database
folder as shown in the following image.-
SoapObjectRepository.java:
This Spring Boot Java class allows to insert in real-time transactional form, each message in ATP, using JDBC driver.//Java Classes USER, CAR, HOUSE Imports import com.oracle_springboot_tutorial.tutorial.model.*; //Spring Boot Imports import org.springframework.stereotype.Repository; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.beans.factory.annotation.Autowired; //Repository Class to save SOAP Messages in the Database @Repository public class SoapObjectRepository { private JdbcTemplate template; private User user; //Getters and Setters for JdbcTemplate template object public JdbcTemplate getTemplate(){ return template; } //Autowired annotation to inject JdbcTemplate object into the template object @Autowired public void setTemplate(JdbcTemplate template){ this.template = template; } //Method to save User SOAP Message in the Database public void saveUserSOAPMessage(User user){ this.user = user; String sql = "INSERT INTO USERS (username, userlastname, id, email, dateuser, attributeuser) VALUES(?, ?, ?, ?, ?, ?)"; template.update(sql, user.getUserName(), user.getUserLastName(), user.getID(), user.getEmail(), user.getDate(), user.getAttribute()); } //Method to save Car SOAP Message in the Database public void saveCarSOAPMessage(Car car){ String sql = "INSERT INTO CARS (userid, brand, color, plate) VALUES(?, ?, ?, ?)"; template.update(sql, user.getID(), car.getBrand(), car.getColor(), car.getPlate()); } //Method to save House SOAP Message in the Database public void saveHouseSOAPMessage(House house){ String sql = "INSERT INTO HOUSES (userid, floors, locationhouse, rooms, bathrooms) VALUES(?, ?, ?, ?, ?)"; template.update(sql, user.getID(), house.getFloors(), house.getLocation(), house.getRooms(), house.getBathRooms()); } }
Now, add the JSON software code, first creating the
json_message
folder and its Java Spring Boot class as shown in the following image. -
JsonBuilder.java:
This Spring Boot Java class converts from SOAP XML format to JSON format.//Imports to be used for JSON import org.json.JSONArray; import org.json.JSONObject; //Imports to be used for the User class import com.oracle_springboot_tutorial.tutorial.model.*; //Imports to be used for the ArrayList class import java.util.ArrayList; public class JsonBuilder { //The buildJsonMessage method creates a JSON object from the ArrayList of User objects public JSONObject buildJsonMessage(ArrayList<User> usersMessageArray) { JSONObject rootJson = new JSONObject(); //Create a new JSON object called rootJson JSONObject messagesJson = new JSONObject(); //Create a new JSON object called messagesJson JSONArray messageArray = new JSONArray(); //Create a new JSON array called messageArray //Iterate through the ArrayList of User objects and create a JSON object for each User object in the ArrayList for (User user : usersMessageArray) { JSONObject messageJson = new JSONObject(); messageJson.put("username", user.getUserName()); //Add the username of the user to the messageJson object messageJson.put("userlastname", user.getUserLastName()); //Add the userlastname of the user to the messageJson object messageJson.put("id", user.getID()); //Add the id of the user to the messageJson object messageJson.put("email", user.getEmail()); //Add the email of the user to the messageJson object messageJson.put("date", user.getDate()); //Add the date of the user to the messageJson object messageJson.put("attribute", user.getAttribute()); //Add the attribute of the user to the messageJson object // JSONObject bodyJson = new JSONObject(); //Create a new JSON object called bodyJson JSONObject envelopeJson = new JSONObject(); //Create a new JSON object called envelopeJson //Switch statement to check the attribute of the User object switch (user.getAttribute()) { case "CAR": Car car = user.getCar(); envelopeJson.put("brand", car.getBrand()); //Add the brand of the car to the envelopeJson object envelopeJson.put("color", car.getColor()); //Add the color of the car to the envelopeJson object envelopeJson.put("plate", car.getPlate()); //Add the plate of the car to the envelopeJson object break; case "HOUSE": House house = user.getHouse(); envelopeJson.put("floors", house.getFloors()); //Add the floors of the house to the envelopeJson object envelopeJson.put("location", house.getLocation()); //Add the location of the house to the envelopeJson object envelopeJson.put("rooms", house.getRooms()); //Add the rooms of the house to the envelopeJson object envelopeJson.put("bathrooms", house.getBathRooms()); //Add the bathrooms of the house to the envelopeJson object break; default: System.out.println("Unknown subject: " + user.getAttribute()); } bodyJson.put("envelope", envelopeJson); //Add the envelopeJson object to the bodyJson object messageJson.put("body", bodyJson); //Add the bodyJson object to the messageJson object messageArray.put(messageJson); //Add the messageJson object to the messageArray array } messagesJson.put("message", messageArray); //Add the messageArray array to the messagesJson object rootJson.put("messages", messagesJson); //Add the messagesJson object to the rootJson object return rootJson; } }
-
-
Now, we can send the messages in JSON format to OCI Queue. Create the
oci_queue
folder and its Java Spring Boot class as shown in the following image.Note: In
OCIQueue.java
class, we will need to define the access from OKE to OCI Queue. For this tutorial, we will use Workloads Access to grant access to OCI resources, without having to handle sensitive information such as user, passwords, OCIDs associated with your tenancy. For more information, see Granting Workloads Access to OCI Resources.Before to start developing
OCIQueue.java
class, we will configure workloads access in our tenancy. First, we need to create a namespace to associate with our Oracle GraalVM based Java application. Ensure you are in admin host.kubectl create ns-tutorial
Then, create a Kubernetes service account for the application.
kubectl create serviceaccount tutorialserviceaccount --namespace ns-tutorial
Now, define an OCI IAM policy to allow the workload to access the necessary OCI resources. In this tutorial, OCI Queue.
Go to the OCI Console, navigate to Identity & Security, Policies and click Create policy. Enter the following information and click Create.
- Name: Enter preferred policy name.
- Description: Enter Access from oke to oci queue.
-
Policy Builder:
Allow any-user to use queues in compartment id ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx where all {request.principal.type = 'workload', request.principal.namespace = 'ns-tutorial', request.principal.service_account = 'tutorialserviceaccount', request.principal.cluster_id = 'ocid1.cluster.oc1.sa-saopaulo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
Once you configure the workload access policy, namespace and service account, we can continue.
In the
application.properties
file, add the queue parameters required to connect and manage the specific OCI Queue created in Task 5.-
OCIQueue.java:
This Spring Boot Java class allows to access and put the messages in OCI Queue.//Imports import com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider; //Import OkeWorkloadIdentityAuthenticationDetailsProvider to enable access and use OCI Workload Identity import com.oracle.bmc.queue.QueueClient; //Import QueueClient to have access and manage of OCI Queue import com.oracle.bmc.queue.model.PutMessagesDetails; //Import PutMessagesDetails to send messages to the OCI Queue import com.oracle.bmc.queue.model.PutMessagesDetailsEntry; //Import PutMessagesDetailsEntry to send messages to the OCI Queue import com.oracle.bmc.queue.requests.PutMessagesRequest; //Import PutMessagesRequest to send messages to the OCI Queue //Imports for the ArrayList and List import java.util.ArrayList; import java.util.List; public class OCIQueue { //Set the required parameters to access to OCI and the Queue //Variables private String queueId; private String endPoint; private String region; //Constructor to initialize the OCI Queue object with the required parameters public OCIQueue(String queueId, String endPoint, String region){ this.queueId = queueId; this.endPoint = endPoint; this.region = region; } //The sendMessages method sends a message to the OCI Queue public void sendMessages(String jsonMessage){ try{ //Create an OkeWorkloadIdentityAuthenticationDetailsProvider object to authenticate the OCI Queue OkeWorkloadIdentityAuthenticationDetailsProvider provider = new OkeWorkloadIdentityAuthenticationDetailsProvider.OkeWorkloadIdentityAuthenticationDetailsProviderBuilder().build(); //Create a QueueClient object to send the message to the OCI Queue QueueClient queueClient = QueueClient.builder().build(provider); queueClient.setRegion(region); queueClient.setEndpoint(endPoint); //Create a PutMessagesDetailsEntry object to send the message PutMessagesDetailsEntry message = PutMessagesDetailsEntry.builder() .content(jsonMessage) .build(); //Create a List of PutMessagesDetailsEntry objects to send the message List<PutMessagesDetailsEntry> messages = new ArrayList<>(); messages.add(message); //Create a PutMessagesDetails object to send the message PutMessagesDetails putMessagesDetails = PutMessagesDetails.builder() .messages(messages) .build(); // Create a PutMessagesRequest object to send the message PutMessagesRequest putMessagesRequest = PutMessagesRequest.builder() .queueId(queueId) .putMessagesDetails(putMessagesDetails) .build(); // Send the request and get the response queueClient.putMessages(putMessagesRequest); }catch(Exception e){ System.out.println("Exception sending message to OCI Queue: "+e); } } }
-
Once you have the Spring Boot Java classes for database, JSON and OCI Queue, we can proceed with
MessagesEndpoint.java
class.For this, we will create a folder called
endpoint
and its Spring Boot Java class.Note: In
MessagesEndpoint.java
, we need to import some autogenerated classes. To do this, add the following source in the Configuration section of thepom.xml
file:<configuration> <sources> <source>${project.build.directory}/generated-sources</source> </sources> </configuration>
The
pom.xml
file should look like.-
MessagesEndpoint.java:
The purpose of this Spring Boot Java class is to extract the SOAP HTTP request and map its values to the User, Car, and House Java objects for each message. It then stores the extracted data in an ATP database for each SOAP XML transaction, converts the data from XML to JSON format, and places the messages in an OCI Queue. These messages can later be retrieved and deleted from the queue by consumers.//Imports import com.oracle_springboot_tutorial.tutorial.model.*; //Import all the classes from the model package import com.oracle_springboot_tutorial.tutorial.oci_queue.OCIQueue; //Import the OCIQueue class from the oci_queue package import com.tutorial_example.ns0.Messages;//Import the Messages class from the tutorial_example.ns0 package (Auto generated Java Classes from the WSDL) import com.tutorial_example.ns0.MessageType; //Import the MessageType class from the tutorial_example.ns0 package (Auto generated Java Classes from the WSDL) //Import the ArrayList class from the java.util package import java.util.ArrayList; //Spring Boot imports to be used for the SOAP Web Service import org.springframework.beans.factory.annotation.Autowired; //Import the @Autowired annotation to inject the SoapObjectRepository object import org.springframework.beans.factory.annotation.Value; //Import the @Value annotation to inject the values from the application.properties file import org.springframework.stereotype.Component; //Import the @Component annotation to register the class with Spring //Spring Boot imports import org.springframework.ws.server.endpoint.annotation.Endpoint; //Import the @Endpoint annotation to register the class with Spring WS import org.springframework.ws.server.endpoint.annotation.PayloadRoot; //Import the @PayloadRoot annotation to specify the namespace URI and local part of the request payload import org.springframework.ws.server.endpoint.annotation.RequestPayload; //Import the @RequestPayload annotation to map the request payload to the method parameter import org.springframework.ws.server.endpoint.annotation.ResponsePayload; //Import the @ResponsePayload annotation to map the returned value to the response payload //Imports to be used storing SOAP information in the database import com.oracle_springboot_tutorial.tutorial.database.SoapObjectRepository; //Import the SoapObjectRepository class from the database package //Imports to be used for JSON import com.oracle_springboot_tutorial.tutorial.json_message.JsonBuilder; //Import the JsonBuilder class from the json_message package import org.json.JSONObject; //Import the JSONObject class from the org.json package //The @Endpoint annotation registers the class with Spring WS. //The @Component annotation registers the class with Spring to be used as a Spring Bean. @Endpoint @Component public class MessagesEndpoint { //Inject not encrypted and decrypted values using jasypt library from the application.properties file @Value("${oci.queue.queueId}") private String queueId; @Value("${oci.queue.endPoint}") private String endPoint; @Value("${oci.queue.region}") private String region; @Value("${spring.datasource.password}") private String datasourcePassword; //The @Autowired loads JDBC template in SoapObjectRepository. @Autowired private SoapObjectRepository soapObjectRepository = new SoapObjectRepository(); //Create a new instance of the JsonBuilder class JsonBuilder jsonBuilder = new JsonBuilder(); //The namespace URI private static final String NAMESPACE_URI = "http://tutorial_example.com/ns0"; //The handleMessagesRequest method is annotated with @PayloadRoot, which means that it is invoked when a request with the specified namespace URI and local part is received. @PayloadRoot(namespace = NAMESPACE_URI, localPart = "messages") //The @ResponsePayload annotation makes Spring WS map the returned value to the response payload. @ResponsePayload //The handleMessagesRequest method processes the request and sends the message to the OCI Queue. public void handleMessagesRequest(@RequestPayload Messages request) { OCIQueue ociQueue = new OCIQueue(queueId, endPoint, region); //Create an ArrayList to store the users ArrayList<User> usersMessageArray = new ArrayList<User>(); //Iterate over the messages, extracting the SOAP Messages and storing in the Java Objects (Car, House, User) for (MessageType message : request.getMessage()) { User user = new User(); user.setUserName(message.getUsername()); user.setUserLastName(message.getUserlastname()); user.setID(message.getId()); user.setEmail(message.getEmail()); user.setDate(message.getDate()); user.setAttribute(message.getAttribute()); //Insert User in Oracle ATP soapObjectRepository.saveUserSOAPMessage(user); //Process the attributes Car or House depending of the kind of User processMessage(user, message); //Add the user to the ArrayList usersMessageArray.add(user); } //Convert to JSON format JSONObject jsonObject = jsonBuilder.buildJsonMessage(usersMessageArray); //Send the JSON message to OCI Queue ociQueue.sendMessages(jsonObject.toString()); } //The processMessage method processes the message based on the user's attribute. private void processMessage(User user, MessageType message) { String subject = user.getAttribute(); switch (subject) { case "CAR": handleCAR(user, message); break; case "HOUSE": handleHouse(user, message); break; default: System.out.println("Unknown subject: " + subject); } } //The handleCAR method processes the CAR message. private void handleCAR(User user, MessageType message) { Car car = new Car(); car.setBrand(message.getBody().getEnvelope().getBrand()); car.setColor(message.getBody().getEnvelope().getColor()); car.setPlate(message.getBody().getEnvelope().getPlate()); user.setCar(car); //Insert Car in Oracle ATP soapObjectRepository.saveCarSOAPMessage(user.getCar()); } //The handleHouse method processes the HOUSE message. private void handleHouse(User user, MessageType message) { House house = new House(); house.setFloors(message.getBody().getEnvelope().getFloors()); house.setLocation(message.getBody().getEnvelope().getLocation()); house.setRooms(message.getBody().getEnvelope().getRooms()); house.setBathRooms(message.getBody().getEnvelope().getBathrooms()); user.setHouse(house); //Insert Houses in Oracle ATP soapObjectRepository.saveHouseSOAPMessage(user.getHouse()); } }
-
-
Now that we have finished the entire construction of the Spring Boot project, we will create the
Dockerfile
in project folder.
-
Dockerfile
:FROM container-registry.oracle.com/graalvm/jdk:17 WORKDIR /app COPY target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Note: The first line of the Docker file pulls and sets the GraalVM JDK image from Oracle’s container registry as the base image. Right from the start, we provide a high-performance JDK, leveraging Just-In-Time (JIT) compilation for optimized execution.
-
Run the following command to build and push the project image in the local Docker repository.
docker build . -t springbootapp:latest
-
Run the following command to verify the image in the local Docker repository.
docker images
-
We can tag the Spring Boot application image with complete path of your OCI Container Registry Classic repository.
docker tag springbootapp:latest gru.ocir.io/xxxxxxxxxx/springboot/tutorialapp:latest
-
Run the following command to verify in the local Docker repository.
docker images
-
Run the following command to push the image to OCI Container Registry Classic.
docker push gru.ocir.io/xxxxxxxxxx/springboot/tutorialapp:latest
-
To review the OKE image application in OCI Container Registry Classic, go to Developer Services, Containers & Artifacts and click Container Registry.
Once the image is in the OCI Container Registry Classic, we can go to our development environment and deploy this image in OKE. For this tutorial, run the following command to create the required configurations.
Note: As the namespace and service account were configured before, it requires the secret.
-
Run the following command to access the project folder.
cd tutorial/
-
Run the following command to create secret for OKE.
kubectl create secret -n ns-tutorial generic ocir --from-file=.dockerconfigjson=../.docker/config.json --type=kubernetes.io/dockerconfigjson
-
-
We already have OKE environment ready so deploy the application image from OCI Container Registry Classic to OKE.
Note: To deploy the application image, it is necessary to have a manifest file. In this tutorial, the following
yaml
file is the manifest file, it is used to deploy the application and create the ingress service represented in an OCI Load Balancer that is listening using80
port.-
springboot_application.yaml
:apiVersion: apps/v1 kind: Deployment metadata: name: soap-oci-queue-app namespace: ns-tutorial labels: app: soap-oci-queue-app spec: replicas: 6 selector: matchLabels: app: soap-oci-queue-app template: metadata: labels: app: soap-oci-queue-app spec: serviceAccountName: tutorialserviceaccount automountServiceAccountToken: true containers: - name: soap-oci-queue-app image: gru.ocir.io/xxxxxxxxxxxx/springboot/tutorialapp:latest ports: - containerPort: 8080 imagePullSecrets: - name: ocir-docker-config --- apiVersion: v1 kind: Service metadata: name: svc-dev-app namespace: ns-tutorial spec: selector: app: soap-oci-queue-app ports: - port: 80 targetPort: 8080 type: LoadBalancer
-
-
Run the
kubectl
command in the folder where you saved the manifest file.kubectl apply -f springboot_application.yaml
Now, the application is deployed and the ingress load balancer service is created in OKE.
-
To validate the pod and service created in OKE, run the following command.
kubectl get pods -A
kubectl get svc -A
Note: Download Spring Boot Oracle GraalVM based Java application project from here: tutorial.zip.
Task 7: Test Spring Boot Oracle Graal VM Application using JMeter
For more information about the JMeter installation, see Getting Started with JMeter.
Once JMeter is installed and configured, you can send HTTP POST SOAP requests. For example, set the number of threads to 2 to represent 2 simultaneous users or applications, and set the Loop Count to 3000, meaning each user or application will send 3000 requests, for a total of 6000 SOAP requests.
In JMeter set our OCI Load Balancer IP, path configured in Spring Boot project and SOAP XML in the body.
Run JMeter with 6000 SOAP transactions, and verify.
Note: As we are simulating the SOAP messaging of client applications, for each SOAP HTTP request the information is the same as seen in the SOAP XML file shown above, and does not change, but in a real customer environment the information will surely vary.
Run the following statement:
-
To see total data stored in ATP.
-
To see details of data stored in each table in ATP.
-
CARS:
-
HOUSES:
-
USERS:
-
-
To see the total OCI Queue requests stored in OCI Queue.
-
To see message details in OCI Queue in JSON format.
Acknowledgments
- Author - Iván Alexander Vásquez Chinome (Oracle LAD A-Team Cloud Solution Specialist)
More Learning Resources
Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.
For product documentation, visit Oracle Help Center.
Create an Oracle GraalVM based Java Application using Spring Boot on OKE to Store SOAP Messages in ATP and Send to OCI Queue
G26974-01
February 2025
Copyright ©2025, Oracle and/or its affiliates.