Remarques :

Créer une application Java basée sur Oracle GraalVM à l'aide de Spring Boot sur OKE pour stocker les messages SOAP dans ATP et les envoyer vers la file d'attente OCI

Introduction

Beaucoup de nos clients s'appuient sur la messagerie SOAP (Simple Object Access Protocol) traditionnelle pour permettre la communication entre leurs applications. Ils doivent souvent stocker des données transactionnelles, assurer le découplage des services, réaliser l'équilibrage de charge des demandes entrantes et permettre la communication asynchrone de ces messages.

Dans ce tutoriel, nous allons voir comment créer une application Java basée sur Oracle GraalVM à l'aide de Spring Boot déployée sur l'infrastructure Oracle Cloud Infrastructure Kubernetes Engine (OKE) qui sera utilisée comme intégrateur transactionnel avec les différents services Oracle Cloud Infrastructure (OCI), tels qu'Oracle Autonomous Transaction Processing (ATP) et OCI Queue. Cette configuration permet aux systèmes d'interagir sans connexions directes, facilitant la communication entre les applications avec des vitesses de traitement différentes. De plus, une fois que les informations sont stockées dans la base de données, elles peuvent être consultées ou analysées.

Nous utiliserons les technologies suivantes :

Services Oracle Cloud Infrastructure (OCI) : OCI est une plate-forme cloud sécurisée et hautes performances offrant plus de 150 services cloud. Il est conçu pour l'évolutivité, la sécurité et les performances.

Technologie Oracle :

Autres technologies :

OCI High Level Architecture :

Architecture OCI

Architecture des cas d'utilisation

Remarque :

Objectifs

Prérequis

Tâche 1 : provisionner et configurer le cluster OKE

Dans cette tâche, nous provisionnerons la plate-forme Kubernetes, où l'application prendra en charge tous les messages de transactionnalité élevée SOAP à stocker dans ATP et les enverra à OCI Queue en temps réel.

  1. Connectez-vous à la console OCI, accédez à Services de développeur, à Clusters Kubernetes (OKE) et sélectionnez le compartiment de votre préférence.

    Il existe deux façons de créer un cluster OKE :

    • Création rapide.
    • Création personnalisée.
  2. Sélectionnez Création rapide car cette méthode est plus facile, plus rapide et déploie automatiquement tous les éléments requis par OKE pour son fonctionnement, par exemple :

    • Réseau cloud virtuel (VCN).
    • Passerelle Internet.
    • Passerelle NAT (Network Address Translation).
    • Passerelle de service.
    • Cluster Kubernetes.
    • Pool de noeuds et noeuds de processus actifs Kubernetes.

    Remarque : pour les environnements d'entreprise, où les clients disposent déjà de services, de réseau et d'infrastructure, il est important de personnaliser le déploiement OKE afin d'être en conformité, d'être aligné sur l'architecture client, les ressources et de suivre les meilleures pratiques.

    Création rapide

  3. Cliquez sur Créer un cluster et entrez les informations suivantes.

    • Nom : entrez le nom du cluster OKE.
    • Compartiment : sélectionnez le compartiment créé pour ce projet.
    • Version de Kubernetes : sélectionnez la dernière version de Kubernetes disponible.
    • Adresse d'API Kubernetes : dans ce tutoriel, sélectionnez Adresse publique, mais vous pouvez également sélectionner Adresse privée.
    • Type de noeud : sélectionnez les noeuds gérés.
    • Noeuds de processus actif Kubernetes : sélectionnez Processus actifs privés.
    • Forme et image : sélectionnez VM.Standard.E5. Flex, personnalisez le nombre d'OCPU (2) et la mémoire (16 Go) et conservez l'image Oracle Linux 8 par défaut.
    • Nombre de noeuds : entrez 2 noeuds de processus actif à déployer avec le pool de noeuds OKE.

    Création d'un cluster OKE

    Vérifiez que le cluster OKE fonctionne.

    OKE en cours d'exécution

Tâche 2 : provisionnement, configuration et accès à OCI Container Registry Classic

Nous devons gérer les images de projet dans un référentiel. Pour ce faire, nous provisionnerons OCI Container Registry Classic. Une fois les images stockées dans OCI Container Registry Classic, nous pourrons les déployer dans OKE.

  1. Accédez à la console OCI, accédez aux services de développeur, aux conteneurs et artefacts, à Container Registry et cliquez sur Créer un référentiel.

  2. Entrez les informations ci-après, puis cliquez sur Créer.

    • Créer dans le compartiment : sélectionnez le compartiment créé pour ce projet.
    • Accès : sélectionnez Public.
    • Nom du référentiel : entrez springboot/tutorialapp.

    Créer un référentiel

  3. Une fois le référentiel créé, accédez-y à partir de l'hôte d'administration Oracle à l'aide de la commande suivante.

    docker login -u 'tenancy_namespace/domain/username' regionID.ocir.io
    
    Password: xxxxxx
    

    Authentification Oracle Registry

    OK

Tâche 3 : provisionnement et configuration d'une base de données sans serveur Oracle Autonomous Transaction Processing (ATP)

Dans la base de données ATP, nous allons stocker les données de chaque message SOAP reçu par transaction, approximativement chacun sera dans l'ordre de millisecondes, en tenant compte des insertions parallèles et séquentielles.

  1. Accédez à la console OCI, accédez à Oracle Database et cliquez sur Autonomous Transaction Processing.

  2. Cliquez sur Créer une instance Autonomous Database et entrez les informations suivantes.

    • Sélectionner le compartiment : sélectionnez le compartiment créé pour ce projet.
    • Nom d'affichage : entrez le nom d'affichage.
    • Afficher le nom de base de données : entrez le nom de base de données.
    • Choisir un type de charge globale : sélectionnez Traitement des transactions.
    • Sélectionnez un type de déploiement : sélectionnez Sans serveur.
    • Configurez la base de données:
      • Développeur : désélectionnez-la.
      • Choisir une version de base de données : sélectionnez 23ai.
      • Nombre d'ECPU : entrez 2.
      • Redimensionnement automatique de calcul : sélectionnez-le.
      • Stockage : entrez 1024 Go.
    • Période de conservation de sauvegarde automatique en jours : conservez l'option par défaut 60 jours.
    • Créez les informations d'identification d'administrateur :
      • Nom utilisateur : est ADMIN par défaut et ne peut pas être modifié.
      • Mot de passe : entrez votre mot de passe préféré.
      • Confirmer le mot de passe : entrez de nouveau ce dernier.
    • Choisir l'accès réseau : sélectionnez Accès à l'adresse privée uniquement, puis choisissez le VCN et le sous-réseau créés pour ce projet. Ce paramètre limite les connexions au réseau privé (VCN) spécifié uniquement. Cependant, vous pouvez choisir d'autres options, cela dépend des besoins de l'entreprise.

    Créer une base de données Oracle ATP

    Créer une base de données Oracle ATP

    Créer une base de données Oracle ATP

    Vérifiez que la base de données ATP est en cours d'exécution.

    DAV en cours d'exécution

Tâche 4 : connexion et création de tables de projet dans Oracle Autonomous Transaction Processing (ATP)

Maintenant, nous devons configurer, connecter et créer les tables de projet dans la base de données ATP qui a été générée dans la tâche 3.

  1. Accédez à la console OCI, accédez à Oracle Database, à Autonomous Transaction Processing et cliquez sur Connexion à la base de données. Sélectionnez Authentification TLS, TLS et cliquez sur Télécharger le portefeuille.

    Connexion de base de données

  2. Décompressez le fichier .zip du portefeuille et, dans tnsnames.ora, vous pouvez obtenir l'URL de source de données pour obtenir la connexion à cette base de données. Enregistrez cette URL de source de données.

    Exemple :

    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)))
    
  3. L'accès à la base de données est désormais nécessaire. Lors du provisionnement du DAV, l'accès à Oracle REST Data Services (ORDS) a été activé. Pour plus d'informations, reportez-vous à Oracle REST Data Services.

    Accédez à la page Détails d'Autonomous Database, puis cliquez sur Actions de base de données. Notez qu'il est accessible uniquement à partir d'une instance de calcul exécutée sur le même réseau cloud virtuel (VCN).

    Database Actions

  4. Collez l'URL dans le navigateur et accédez à ORDS à l'aide de l'utilisateur et du mot de passe saisis précédemment dans la base de données ATP et accédez au module Oracle SQL Developer Web.

    ORDS

  5. Utilisez les requêtes suivantes pour créer les tables USERS, CARS et HOUSES associées aux messages SOAP que nous recevrons.

    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
    );
    

    Tables créées dans la base de données

Tâche 5 : provisionnement et configuration de la file d'attente OCI

Dans la file d'attente OCI, nous stockons des messages hautement transactionnels via l'API HTTP RESTful pendant une période spécifique. Les consommateurs peuvent ensuite lire et supprimer les messages immédiatement ou à leur convenance, en assurant le découplage et en évitant la perte de données.

  1. Accédez à la console OCI, accédez à Services de développeur, à Intégration d'application et cliquez sur Files d'attente.

  2. Cliquez sur Créer une file d'attente, entrez les informations suivantes et cliquez sur Créer une file d'attente.

    • Nom : entrez le nom approprié pour la file d'attente.
    • Compartiment : sélectionnez le compartiment de travail.
    • Paramètres de file d'attente : dans ce tutoriel, nous allons sélectionner Configuration par défaut, mais si vous préférez, vous pouvez personnaliser plusieurs options en fonction des besoins de l'entreprise, par exemple : Délai d'expiration de visibilité, Période de conservation maximale, Consommation maximale de canal et File d'attente de lettres mortes.
    • Configurer les paramètres de cryptage : sélectionnez Clé gérée par Oracle, mais la clé gérée par le client est également une option.

    Page Création d'une file d'attente

    Vérifiez que la file d'attente OCI est en cours d'exécution.

    File d'attente OCI en cours d'exécution

Tâche 6 : créer une application Java basée sur Oracle GraalVM avec Spring Boot et la déployer dans OKE

Désormais, nous allons développer et déployer une application Java basée sur Oracle GraalVM sur Spring Boot qui effectuera les tâches suivantes :

Remarque : avant de commencer, il est important de créer un hôte d'administration et un environnement de développement, comme indiqué dans les sections Prérequis - Hôte d'administration et Prérequis - Environnement de développement.

Une fois que votre hôte d'administration et votre environnement de développement sont configurés et prêts, vous pouvez commencer à développer votre projet Spring Boot.

  1. Accédez à Spring initializr et créez le premier projet qui nous donnera la structure des dossiers et les fichiers de base d'un projet Spring Boot, pour le modifier ultérieurement en fonction de nos exigences. Entrez les informations suivantes et cliquez sur Générer pour télécharger automatiquement le projet Spring Boot, l'enregistrer et le décompresser dans l'hôte de développement.

    • Projet : sélectionnez Maven.
    • Langue : sélectionnez Java.
    • Initialisation Spring : sélectionnez 3.3.6.
    • Métadonnées du projet:
      • Groupe : entrez com.tutorial_springboot.
      • Artefact : entrez tutoriel.
      • Nom : entrez tutorial.
      • Description : entrez Application Spring Boot (lire SOAP, transformer en JSON, insérer ATP et placer dans la file d'attente OCI).
    • Package : sélectionnez Jar.
    • Java : sélectionnez 17.
    • Dépendances : sélectionnez Pilote Oracle, Services Web Spring et Web Spring.

    Remarque : nous pouvons ajouter des dépendances dans le projet, puis en ajouter d'autres, directement dans le fichier pom.xml, en fonction de nos besoins.

    Projet Springboot

  2. Nous avons maintenant le projet de structure de démarrage à ressort.

    Structure Spring Boot Project

    Passez en revue le fichier pom.xml. Nous allons commencer à travailler dessus.

    Fichier POM d'origine

    Mettez à jour le fichier pom.xml en fonction de la portée proposée dans ce tutoriel.

    • Ajoutez la version oci sdk et les dépendances suivantes.

      • oci-java-sdk-common.
      • oci-java-sdk-queue.
      • oci-java-sdk-addons-oke-workload-identity.
      • oci-java-sdk-common-httpclient-jersey3.

      L'application devra s'authentifier auprès d'OCI, se connecter et gérer les services OCI, tels qu'OCI Queue.

    • Notre fichier pom.xml contient déjà la dépendance spring-boot-starter-web-services et nous devons ajouter la dépendance wsdl4j. L'objectif principal est d'obtenir les données reçues des messages SOAP et de les placer dans des objets Java, en créant des services Web Spring manipulant les charges utiles XML, afin de faciliter le développement de services SOAP en priorité sur contrat. Permet également de configurer le port, l'URI et le schéma XML défini chargés à partir du fichier XSD (XML Schema Definition).

    • Ajoutez une dépendance JSON. Cette bibliothèque sera utilisée pour générer le format JSON avec les données extraites du message SOAP.

    • Ajoutez le module d'extension spring-boot-maven-plugin dans la section Construction. Ce plugin nous permettra de générer le fichier de projet Spring Boot exécutable jar.

    • Ajoutez le module d'extension jaxb2-maven-plugin dans la section Construction. Ce module d'extension utilisera l'API Java pour la liaison XML (JAXB), afin de générer des classes Java à partir de schémas XML et de cette manière, nous pouvons transmettre les données du message SOAP aux objets de classe Java que nous avons créés.

      Dans cette section de plugin, il est important de mettre la configuration qui indiquera le chemin, où le fichier XSD est inclus dans notre projet Spring Boot.

      <configuration>
         <sources>
         <source>${project.basedir}/src/main/resources/messages.xsd<source>
         </sources>
      </configuration>
      
    • Ajoutez la dépendance jasypt-spring-boot-starter dans la section Dépendances et le module d'extension jasypt-maven-plugin dans la section Construction, ce qui nous permettra de crypter les paramètres sensibles dans le fichier application.properties, garantissant ainsi une utilisation sécurisée dans notre application.

    Vérifiez les dépendances suivantes ajoutées dans le fichier pom.xml.

    Fichier POM modifié

    Fichier POM modifié

  3. Téléchargez les bibliothèques et exécutez les commandes suivantes.

    1. Dans votre environnement de développement, exécutez la commande suivante pour accéder à votre projet.

      cd tutorial
      

      Dossier du projet d'initialisation Spring

    2. Nettoyez le projet et supprimez tous les fichiers générés par le build précédent.

      mvn clean
      

      Nettoyage Maven

    3. Purger (supprimer et éventuellement résoudre) les artefacts à partir du référentiel Maven local.

      mvn dependency:purge-local-repository
      

    Dépendances de purge Maven

  4. Nous avons déjà les dépendances et le fichier pom.xml configurés dans notre projet, nous allons vérifier le fichier XML SOAP, car il représente la demande du côté du client, et le fichier XSD, qui interprète la demande du côté du projet Spring Boot.

    1. Ce fichier XML SOAP comporte deux messages, avec des informations personnelles et d'autres types d'attributs provenant de deux clients différents que nous enverrons par demande, comme illustré dans l'image suivante.

      Fichier de savon

    2. Maintenant, côté projet Spring Boot, un schéma XML est nécessaire pour définir un domaine de service Web que le service Web Spring exporte automatiquement en tant que WSDL, l'image suivante montre le fichier messages.xsd défini pour ce tutoriel.

      messages.xsd:

      fichier XSD

    3. Enregistrez le fichier messages.xsd dans le dossier de ressources de votre projet Spring Boot.

      Fichier XSD dans le dossier

  5. Créez et installez des fichiers de projet dans un fichier JAR. Exécutez la commande suivante et assurez-vous que vous êtes dans votre dossier de projet Spring Boot.

    mvn install
    

    Remarque : une fois la commande d'installation maven exécutée, le dossier cible est généré automatiquement et, de la même manière, les classes Java selon le fichier XSD créé précédemment et le fichier exécutable .jar du projet.

    Dossier cible

  6. Maintenant, nous pouvons ajouter les classes Java requises à notre projet Spring Boot.

    WebServiceConfig.java Class: Cette classe Java a été développée pour créer un service Web SOAP :

    • Configure un servlet pour gérer les demandes SOAP.
    • Génère une définition WSDL basée sur un schéma XML.
    • Définit l'adresse d'accès du service Web SOAP.
    • Utilise le fichier de schéma messages.xsd à partir du 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
       }
    }
    

    Remarque : si vous voulez tester le service Web, vous pouvez exécuter le projet Spring Boot sur le même bureau d'environnement de développement local et envoyer une demande HTTP à l'aide de curl, comme suit :

    mvn spring-boot:run
    

    Exécuter le projet d'initialisation Spring

    Exécuter le projet d'initialisation Spring

    Une fois le projet en cours d'exécution et le service Web démarré, exécutez une demande HTTP SOAP locale à l'aide de curl, comme suit :

    curl --location 'http://localhost:8080/ws/'
    

    Et vous obtiendrez une réponse du service Web exposé dans notre projet Spring Boot.

    Service Web SOAP

  7. Créez un dossier nommé model. Dans ce dossier, nous ajouterons les classes Java suivantes.

    Dossier de modèles

    Remarque : ces classes Java Car, House et User extraient les informations en fonction des données de chaque message SOAP extrait de la demande SOAP HTTP.

    • Car.java class: Cette classe Java représente un objet Car dont les attributs sont liés à chaque utilisateur.

      ```
      //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: Cette classe Java représente un objet Maison dont les attributs sont liés à chaque utilisateur.

        //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: Cette classe Java représente un objet Utilisateur avec ses attributs, contenant les objets Car et House.

        //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;
           }
        }
        
  8. Nous allons maintenant configurer les paramètres nécessaires dans notre projet Spring Boot pour stocker les données obtenues dans la base de données OCI ATP. Dans le dossier resources, vous devez trouver le fichier application.properties utilisé pour ajouter les paramètres requis par l'application. Il est automatiquement généré et chargé par Spring Boot au démarrage de l'application.

    Remarque : il est très important de gérer les méthodes de cryptage et de sécurité qui garantissent que les informations sensibles, telles que les mots de passe ou les données pertinentes, ne peuvent pas être extraites ou consultées par les pirates. Dans ce tutoriel, nous utilisons la bibliothèque jasypt configurée dans le fichier pom.xml. Pour plus d'informations, reportez-vous à la section How to encrypt words in a Spring Boot project using Jasypt. De plus, dans nos classes Java sur Spring Boot, il est indiqué où ajouter les annotations et le code source associés à cette bibliothèque pour déchiffrer les paramètres application.properties.

    Ajoutez les paramètres appropriés requis par la base de données ATP dans le fichier application.properties, comme indiqué dans l'image suivante.

    Fichier de propriétés de l'application

    Créez la classe Java Spring Boot pour stocker chaque message dans la base de données. Elle se trouvera dans le dossier database, comme indiqué dans l'image suivante.

    Database Folder

    • SoapObjectRepository.java: Cette classe Java Spring Boot permet d'insérer sous forme transactionnelle en temps réel, chaque message dans ATP, à l'aide du pilote JDBC.

      //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());
         }
      
      
      }
      

      A présent, ajoutez le code logiciel JSON en créant d'abord le dossier json_message et sa classe Java Spring Boot, comme illustré dans l'image suivante.

      Dossier Json

    • JsonBuilder.java: Cette classe Java Spring Boot est convertie du format XML SOAP au format JSON.

      //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;
         }
      }
      
  9. Désormais, nous pouvons envoyer les messages au format JSON à OCI Queue. Créez le dossier oci_queue et sa classe Java Spring Boot comme indiqué dans l'image suivante.

    Dossier de file d'attente

    Remarque : dans la classe OCIQueue.java, nous devrons définir l'accès d'OKE à OCI Queue. Dans ce tutoriel, nous allons utiliser Accès aux charges globales pour accorder l'accès aux ressources OCI, sans avoir à gérer les informations sensibles telles que l'utilisateur, les mots de passe et les OCID associés à votre location. Pour plus d'informations, reportez-vous à Octroi de l'accès des charges globales aux ressources OCI.

    Avant de commencer à développer la classe OCIQueue.java, nous configurons l'accès aux charges globales dans notre location. Tout d'abord, nous devons créer un espace de noms à associer à notre application Java basée sur Oracle GraalVM. Assurez-vous que vous êtes dans l'hôte d'administration.

    kubectl create ns-tutorial
    

    Créez ensuite un compte de service Kubernetes pour l'application.

    kubectl create serviceaccount tutorialserviceaccount --namespace ns-tutorial
    

    Définissez maintenant une stratégie OCI IAM pour permettre à la charge globale d'accéder aux ressources OCI nécessaires. Dans ce tutoriel, OCI Queue.

    Accédez à la console OCI, accédez à Identité et sécurité, à Stratégies et cliquez sur Créer une stratégie. Entrez les informations ci-après, puis cliquez sur Créer.

    • Nom : entrez le nom de la stratégie préférée.
    • Description : entrez Accès d'Oke à la file d'attente OCI.
    • Générateur de stratégies:

      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'}
      

    Stratégie d'accès aux charges globales

    Une fois que vous avez configuré la stratégie d'accès à la charge globale, l'espace de noms et le compte de service, nous pouvons continuer.

    Dans le fichier application.properties, ajoutez les paramètres de file d'attente requis pour la connexion et la gestion de la file d'attente OCI spécifique créée dans la tâche 5.

    Paramètres OCIID

    • OCIQueue.java: Cette classe Java Spring Boot permet d'accéder aux messages et de les placer dans la file d'attente OCI.

      //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);
            }
         }
      }
      
  10. Une fois que vous disposez des classes Java Spring Boot pour la base de données, JSON et OCI Queue, nous pouvons passer à la classe MessagesEndpoint.java.

    Pour cela, nous allons créer un dossier nommé endpoint et sa classe Java Spring Boot.

    Dossier d'adresses

    Remarque : dans MessagesEndpoint.java, nous devons importer certaines classes générées automatiquement. Pour ce faire, ajoutez la source suivante dans la section Configuration du fichier pom.xml :

    <configuration>
       <sources>
       <source>${project.build.directory}/generated-sources</source>
       </sources>
    </configuration>
    

    Le fichier pom.xml doit ressembler à ceci :

    Chemin des sources générées

    • MessagesEndpoint.java: Le but de cette classe Java Spring Boot est d'extraire la demande HTTP SOAP et de mettre en correspondance ses valeurs avec les objets Java Utilisateur, Voiture et Maison pour chaque message. Il stocke ensuite les données extraites dans une base de données ATP pour chaque transaction XML SOAP, convertit les données du format XML au format JSON et place les messages dans une file d'attente OCI. Ces messages peuvent ensuite être extraits et supprimés de la file d'attente par les destinataires.

      //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());
      
         }
      }
      
  11. Maintenant que nous avons terminé la construction complète du projet Spring Boot, nous allons créer le fichier Dockerfile dans le dossier du projet.

  1. Exécutez la commande suivante pour créer et propager l'image de projet dans le référentiel Docker local.

    docker build . -t springbootapp:latest
    

    Créer une image Docker

  2. Exécutez la commande suivante pour vérifier l'image dans le référentiel Docker local.

    docker images
    

    Repo local d'image Docker

  3. Nous pouvons baliser l'image d'application Spring Boot avec le chemin complet de votre référentiel OCI Container Registry Classic.

    docker tag springbootapp:latest gru.ocir.io/xxxxxxxxxx/springboot/tutorialapp:latest
    
  4. Exécutez la commande suivante pour effectuer une vérification dans le référentiel Docker local.

    docker images
    

    Balise de l'application OKE

  5. Exécutez la commande suivante pour propager l'image vers OCI Container Registry Classic.

    docker push gru.ocir.io/xxxxxxxxxx/springboot/tutorialapp:latest
    

    Push de l'application OKE

  6. Pour consulter l'application d'image OKE dans OCI Container Registry Classic, accédez à Services de développeur, à Conteneurs et artefacts, puis cliquez sur Container Registry.

    Image dans le registre de conteneurs OKE

    Une fois l'image dans OCI Container Registry Classic, nous pouvons accéder à notre environnement de développement et la déployer dans OKE. Pour ce tutoriel, exécutez la commande suivante pour créer les configurations requises.

    Remarque : comme l'espace de noms et le compte de service ont été configurés précédemment, la clé secrète est requise.

    1. Exécutez la commande suivante pour accéder au dossier du projet.

      cd tutorial/
      
    2. Exécutez la commande suivante pour créer une clé secrète pour OKE.

      kubectl create secret -n ns-tutorial generic ocir --from-file=.dockerconfigjson=../.docker/config.json --type=kubernetes.io/dockerconfigjson
      

      Créer une clé secrète

  7. L'environnement OKE est déjà prêt. Déployez donc l'image d'application d'OCI Container Registry Classic vers OKE.

    Remarque : pour déployer l'image de l'application, vous devez disposer d'un fichier manifeste. Dans ce tutoriel, le fichier yaml suivant est le fichier manifeste. Il est utilisé pour déployer l'application et créer le service entrant représenté dans un équilibreur de charge OCI qui écoute à l'aide du port 80.

    • 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
      
  8. Exécutez la commande kubectl dans le dossier où vous avez enregistré le fichier manifeste.

    kubectl apply -f springboot_application.yaml
    

    A présent, l'application est déployée et le service d'équilibreur de charge entrant est créé dans OKE.

    Appliquer le manifeste dans OKE

  9. Pour valider le pod et le service créés dans OKE, exécutez la commande suivante.

    kubectl get pods -A
    

    PODS OKE

    kubectl get svc -A
    

    Services OKE

    Remarque : téléchargez le projet d'application Java basé sur Oracle GraalVM Spring Boot à partir de l'adresse suivante : tutorial.zip.

Tâche 7 : test de Spring Boot de l'application Oracle Graal VM à l'aide de JMeter

Pour plus d'informations sur l'installation de JMeter, reportez-vous à Mise en route de JMeter.

Une fois JMeter installé et configuré, vous pouvez envoyer des demandes SOAP HTTP POST. Par exemple, définissez le nombre de threads sur 2 pour représenter 2 utilisateurs ou applications simultanés, et définissez le nombre de boucles sur 3000, ce qui signifie que chaque utilisateur ou application enverra 3000 demandes, pour un total de 6000 demandes SOAP.

Messages SOAP

Dans JMeter, définissez l'adresse IP de l'équilibreur de charge OCI, le chemin configuré dans le projet Spring Boot et le code XML SOAP dans le corps.

Demande SOAP HTTP

Exécutez JMeter avec 6000 transactions SOAP et vérifiez.

Remarque : pendant que nous simulons la messagerie SOAP des applications client, pour chaque demande HTTP SOAP, les informations sont les mêmes que dans le fichier XML SOAP indiqué ci-dessus. Elles ne changent pas, mais dans un environnement client réel, les informations varient sûrement.

Exécutez l'instruction suivante :

  1. Pour voir le total des données stockées dans ATP.

    Oracle ATP

  2. Pour afficher les détails des données stockées dans chaque table dans ATP.

    • VOITURES :

      VOITURES

    • MAISONS :

      MAISONS

    • UTILISATEURS :

      UTILISATEURS

  3. Pour afficher le nombre total de demandes de file d'attente OCI stockées dans la file d'attente OCI, procédez comme suit :

    Nombre total de demandes de file d'attente OCI

  4. Pour afficher les détails des messages dans la file d'attente OCI au format JSON.

    Détails des messages de file d'attente OCI

Remerciements

Ressources de formation supplémentaires

Explorez d'autres ateliers sur docs.oracle.com/learn ou accédez à d'autres contenus de formation gratuits sur le canal Oracle Learning YouTube. De plus, visitez le site education.oracle.com/learning-explorer pour devenir un explorateur Oracle Learning.

Pour obtenir la documentation produit, consultez le site Oracle Help Center.