Techland https://techland.info/ Blog dédié à la tech en général Hugo 0.153.2 & FixIt v0.4.0-alpha-20250805041424-57ccd470 fr [email protected] (LE BARO Romain) [email protected] (LE BARO Romain) Thu, 25 Dec 2025 15:10:00 +0100 OAuth 2.1 - une évolution pas si anodine https://techland.info/posts/security/oauth2-1-the-new-feature/ Thu, 25 Dec 2025 15:10:00 +0100 https://techland.info/posts/security/oauth2-1-the-new-feature/ Sécurité <p><a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1"target="_blank" rel="external nofollow noopener noreferrer">Oauth 2.1</a> est encore en draft mais l&rsquo;on peut déjà voir les évolutions qui arriveront par rapport à OAuth 2.0. OAuth 2.1 est décrit comme une consolidation des bonnes pratiques et des extensions ajoutées à OAuth 2.0. Nous allons parler d&rsquo;une fonctionnalité en particulier qui à mon sens est un gros plus, mais n&rsquo;est, pourtant pas du tout mis en avant.</p> <h2 class="heading-element" id="les-nouveautés-de-oauth-21"><span>Les nouveautés de OAuth 2.1</span> <a href="#les-nouveaut%c3%a9s-de-oauth-21" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Sur le site de la spécification les principaux changements mis en avant sont les suivants :</p> <ul> <li>PKCE est maintenant requis pour tous les clients OAuth utilisant le flow <code>Authorization Code</code></li> <li>Les <code>Redirect URIs</code> doivent être comparées avec une égalité parfaite. Plus de wildcard (<code>*</code>)</li> <li>L&rsquo; <code>Implicit grant</code> (response_type=token) est supprimé de la spécification</li> <li>Le <code>Resource Owner Password Credentials grant</code> est supprimé de la spécification</li> <li>Les <code>Bearer token</code> ne sont plus utilisables dans les paramètres des URIs.</li> <li>Les Refresh tokens pour les clients publics doivent être soit must contraint par l&rsquo;expéditeur soit utilisable une unique fois.</li> <li>Les définitions de clients publics et confidentiels ont été simplifiés et ne font plus référence qu&rsquo;à la possession ou non d&rsquo;une accréditation par le client.</li> </ul> <p>Comme je le disais en intro, cela ne fait que pérenniser des bonnes pratiques déjà employées avec OAuth2.</p> <h2 class="heading-element" id="header-www-authenticate-le-header-qui-change-tout"><span>Header WWW-Authenticate, le header qui change tout.</span> <a href="#header-www-authenticate-le-header-qui-change-tout" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Une nouveauté qui pourrait presque passer inaperçu mais qui change pour moi beaucoup de chose est la section <a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#name-the-www-authenticate-respon"target="_blank" rel="external nofollow noopener noreferrer">5.3.1. The WWW-Authenticate Response Header Field</a>.</p> <p>Le concept est qu&rsquo;en cas d&rsquo;échec lors de l&rsquo;appel d&rsquo;un endpoint, une réponse avec un code 401/403 est renvoyé et un header WWW-Authenticate doit ajouté à la réponse. Ce Header peut avoir les attributs suivants :</p> <ul> <li><strong>realm</strong>: Le royaume gérant les authorizations pour le serveur de ressource</li> <li><strong>scope</strong>: La liste des scopes requis pour accéder au endpoint demandé</li> <li><strong>error</strong>: Un code d&rsquo;erreur. Les valeurs possibles sont <code>invalid_request</code>,<code>invalid_token</code>, <code>insufficient_scope</code></li> <li><strong>error_description</strong>: Une description de l&rsquo;erreur</li> <li><strong>error_uri</strong>: Un lien permettant d&rsquo;avoir de plus amples information sur l&rsquo;erreur</li> </ul> <p>Par exemple en cas de code 401 pour une requète sans token :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">401</span> <span class="ne">Unauthorized</span> </span></span><span class="line"><span class="cl"><span class="n">WWW-Authenticate</span><span class="o">:</span> <span class="l">Bearer realm=&#34;https://auth.example.com&#34;</span></span></span></code></pre></td></tr></table> </div> </div><p>Ou avec un token expiré :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a> </span><span class="lnt" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a> </span><span class="lnt" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a> </span><span class="lnt" id="hl-1-4"><a class="lnlinks" href="#hl-1-4">4</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">401</span> <span class="ne">Unauthorized</span> </span></span><span class="line"><span class="cl"><span class="n">WWW-Authenticate</span><span class="o">:</span> <span class="l">Bearer realm=&#34;https://auth.example.com&#34;,</span> </span></span><span class="line"><span class="cl"> <span class="l">error=&#34;invalid_token&#34;,</span> </span></span><span class="line"><span class="cl"> <span class="l">error_description=&#34;The access token expired&#34;</span></span></span></code></pre></td></tr></table> </div> </div><p>Dans le cas où le token est valide, mais ne dispose pas des scopes suffisants :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a> </span><span class="lnt" id="hl-2-2"><a class="lnlinks" href="#hl-2-2">2</a> </span><span class="lnt" id="hl-2-3"><a class="lnlinks" href="#hl-2-3">3</a> </span><span class="lnt" id="hl-2-4"><a class="lnlinks" href="#hl-2-4">4</a> </span><span class="lnt" id="hl-2-5"><a class="lnlinks" href="#hl-2-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">403</span> <span class="ne">Forbidden</span> </span></span><span class="line"><span class="cl"><span class="n">WWW-Authenticate</span><span class="o">:</span> <span class="l">Bearer realm=&#34;https://auth.example.com&#34;,</span> </span></span><span class="line"><span class="cl"> <span class="l">error=&#34;insufficient_scope&#34;,</span> </span></span><span class="line"><span class="cl"> <span class="l">error_description=&#34;Bearer token is missing required scopes&#34;</span> </span></span><span class="line"><span class="cl"> <span class="l">scope=&#34;openid user-list user-add&#34;</span></span></span></code></pre></td></tr></table> </div> </div><p>Ces apports à priori anodins permettent de découpler complétement un client des serveurs de ressource d&rsquo;un point de vue autorisation d&rsquo;accès. Le serveur de ressource est maintenant capable d&rsquo;indiquer auprès de quel royaume demander un token et quels scopes celui-ci doit contenir pour être valide.</p> <p>Cela permet de rendre complétement dynamique la gestion des droits sans avoir à redéployer une mise à jour des clients. Ceux-ci iront automatiquement chercher les nouveaux scopes nécessaire.</p> <p>La spécification ne précise pas comment le serveur de ressource récupère la liste des scopes à renseigner dans le token. Le plus simple est de passer par un système de convention. Ainsi le endpoint <code>POST /users</code> aura besoin du scope <code>user-add</code>. une autre solution est d&rsquo;avoir un endpoint exposé par le serveur d&rsquo;autorisation que le serveur de ressource pourrait interroger pour connaitre la liste des scopes nécessaire pour un endpoint.</p> <p>Dans le deuxième cas, un manque de standardisation entre les serveurs d&rsquo;autorisation se fera rapidement ressentir et on se tournera volontiers vers une extension OAuth, appelée UMA 2, répondant justement à ces problématiques et proposant le même mécanisme de header <code>WWW-Authenticate</code>.</p> <pre class="mermaid">--- config: noteAlign: left --- sequenceDiagram autonumber participant Client participant ResourceServer participant AuthorizationServer Client->>ResourceServer: Initial request without token or with missing scope alt Requesting scope to AS ResourceServer->>AuthorizationServer: Ask for required scopes AuthorizationServer->>ResourceServer: Send Required scopes else Handling required scope locally ResourceServer->>ResourceServer: compute scopes via convention or configuration end ResourceServer->>Client: 403 - WWW-Autenticate header (realm + scope + error) Note left of ResourceServer: HTTP/1.1 403 Forbidden <br/><br/> WWW-Authenticate: Bearer realm="https://auth.example.com",<br> error="insufficient_scope",<br>error_description="Bearer token is missing required scopes"<br>scope="openid user-list user-add" Client->>AuthorizationServer: Ask for a token with required scope AuthorizationServer->>Client: Check Rights and send token with requested scopes Client->>ResourceServer: Send request with token with needed scopes ResourceServer->>Client: Send requested data</pre> <template>--- config: noteAlign: left --- sequenceDiagram autonumber participant Client participant ResourceServer participant AuthorizationServer Client->>ResourceServer: Initial request without token or with missing scope alt Requesting scope to AS ResourceServer->>AuthorizationServer: Ask for required scopes AuthorizationServer->>ResourceServer: Send Required scopes else Handling required scope locally ResourceServer->>ResourceServer: compute scopes via convention or configuration end ResourceServer->>Client: 403 - WWW-Autenticate header (realm + scope + error) Note left of ResourceServer: HTTP/1.1 403 Forbidden <br/><br/> WWW-Authenticate: Bearer realm="https://auth.example.com",<br> error="insufficient_scope",<br>error_description="Bearer token is missing required scopes"<br>scope="openid user-list user-add" Client->>AuthorizationServer: Ask for a token with required scope AuthorizationServer->>Client: Check Rights and send token with requested scopes Client->>ResourceServer: Send request with token with needed scopes ResourceServer->>Client: Send requested data</template><h2 class="heading-element" id="conclusion"><span>Conclusion</span> <a href="#conclusion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>OAuth 2.1 est une évolution attendue de longue date et tirant profit des bonnes pratiques et l&rsquo;expérience d&rsquo;OAuth2. L&rsquo;apport du header WWW-Autenticate de manière standard dans la spécification permet d&rsquo;envisager une réduction de la maintenance des applications grâce à une prise en compte dynamique des changements d&rsquo;autorisations des serveurs de ressources.</p> Conditional Authenticator personnalisé avec Keycloak https://techland.info/posts/security/keycloak-custom-authenticator/ Sun, 10 Aug 2025 12:00:00 +0100 https://techland.info/posts/security/keycloak-custom-authenticator/ Sécurité <p>Dans Keycloak, quand vous avez besoin de personnalisé un flow de connexion suivant certaines conditions non prévue de base par Keycloak (User configured et User Attribute), la manière de procéder est d&rsquo;implémenter un <code>ConditionalAuthenticator</code></p> <h2 class="heading-element" id="prérequis"><span>Prérequis</span> <a href="#pr%c3%a9requis" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Nous allons avoir besoin des dépendances suivantes :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a> </span><span class="lnt" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a> </span><span class="lnt" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a> </span><span class="lnt" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a> </span><span class="lnt" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a> </span><span class="lnt" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a> </span><span class="lnt" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a> </span><span class="lnt" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a> </span><span class="lnt" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a> </span><span class="lnt" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a> </span><span class="lnt" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;dependency&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;groupId&gt;</span>org.keycloak<span class="nt">&lt;/groupId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;artifactId&gt;</span>keycloak-server-spi<span class="nt">&lt;/artifactId&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/dependency&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;dependency&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;groupId&gt;</span>org.keycloak<span class="nt">&lt;/groupId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;artifactId&gt;</span>keycloak-server-spi-private<span class="nt">&lt;/artifactId&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/dependency&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;dependency&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;groupId&gt;</span>org.keycloak<span class="nt">&lt;/groupId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;artifactId&gt;</span>keycloak-services<span class="nt">&lt;/artifactId&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/dependency&gt;</span></span></span></code></pre></td></tr></table> </div> </div><h2 class="heading-element" id="création-de-la-factory"><span>Création de la factory</span> <a href="#cr%c3%a9ation-de-la-factory" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Keycloak utilise le mécanisme Java standard des SPIs pour définir ses providers. Nous devons donc créer notre <code>factory</code> permettant à Keycloak d&rsquo;initialiser notre provider.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a> </span><span class="lnt" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a> </span><span class="lnt" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a> </span><span class="lnt" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a> </span><span class="lnt" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a> </span><span class="lnt" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a> </span><span class="lnt" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a> </span><span class="lnt" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a> </span><span class="lnt" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a> </span><span class="lnt" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a> </span><span class="lnt" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a> </span><span class="lnt" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a> </span><span class="lnt" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a> </span><span class="lnt" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a> </span><span class="lnt" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a> </span><span class="lnt" id="hl-1-16"><a class="lnlinks" href="#hl-1-16">16</a> </span><span class="lnt" id="hl-1-17"><a class="lnlinks" href="#hl-1-17">17</a> </span><span class="lnt" id="hl-1-18"><a class="lnlinks" href="#hl-1-18">18</a> </span><span class="lnt" id="hl-1-19"><a class="lnlinks" href="#hl-1-19">19</a> </span><span class="lnt" id="hl-1-20"><a class="lnlinks" href="#hl-1-20">20</a> </span><span class="lnt" id="hl-1-21"><a class="lnlinks" href="#hl-1-21">21</a> </span><span class="lnt" id="hl-1-22"><a class="lnlinks" href="#hl-1-22">22</a> </span><span class="lnt" id="hl-1-23"><a class="lnlinks" href="#hl-1-23">23</a> </span><span class="lnt" id="hl-1-24"><a class="lnlinks" href="#hl-1-24">24</a> </span><span class="lnt" id="hl-1-25"><a class="lnlinks" href="#hl-1-25">25</a> </span><span class="lnt" id="hl-1-26"><a class="lnlinks" href="#hl-1-26">26</a> </span><span class="lnt" id="hl-1-27"><a class="lnlinks" href="#hl-1-27">27</a> </span><span class="lnt" id="hl-1-28"><a class="lnlinks" href="#hl-1-28">28</a> </span><span class="lnt" id="hl-1-29"><a class="lnlinks" href="#hl-1-29">29</a> </span><span class="lnt" id="hl-1-30"><a class="lnlinks" href="#hl-1-30">30</a> </span><span class="lnt" id="hl-1-31"><a class="lnlinks" href="#hl-1-31">31</a> </span><span class="lnt" id="hl-1-32"><a class="lnlinks" href="#hl-1-32">32</a> </span><span class="lnt" id="hl-1-33"><a class="lnlinks" href="#hl-1-33">33</a> </span><span class="lnt" id="hl-1-34"><a class="lnlinks" href="#hl-1-34">34</a> </span><span class="lnt" id="hl-1-35"><a class="lnlinks" href="#hl-1-35">35</a> </span><span class="lnt" id="hl-1-36"><a class="lnlinks" href="#hl-1-36">36</a> </span><span class="lnt" id="hl-1-37"><a class="lnlinks" href="#hl-1-37">37</a> </span><span class="lnt" id="hl-1-38"><a class="lnlinks" href="#hl-1-38">38</a> </span><span class="lnt" id="hl-1-39"><a class="lnlinks" href="#hl-1-39">39</a> </span><span class="lnt" id="hl-1-40"><a class="lnlinks" href="#hl-1-40">40</a> </span><span class="lnt" id="hl-1-41"><a class="lnlinks" href="#hl-1-41">41</a> </span><span class="lnt" id="hl-1-42"><a class="lnlinks" href="#hl-1-42">42</a> </span><span class="lnt" id="hl-1-43"><a class="lnlinks" href="#hl-1-43">43</a> </span><span class="lnt" id="hl-1-44"><a class="lnlinks" href="#hl-1-44">44</a> </span><span class="lnt" id="hl-1-45"><a class="lnlinks" href="#hl-1-45">45</a> </span><span class="lnt" id="hl-1-46"><a class="lnlinks" href="#hl-1-46">46</a> </span><span class="lnt" id="hl-1-47"><a class="lnlinks" href="#hl-1-47">47</a> </span><span class="lnt" id="hl-1-48"><a class="lnlinks" href="#hl-1-48">48</a> </span><span class="lnt" id="hl-1-49"><a class="lnlinks" href="#hl-1-49">49</a> </span><span class="lnt" id="hl-1-50"><a class="lnlinks" href="#hl-1-50">50</a> </span><span class="lnt" id="hl-1-51"><a class="lnlinks" href="#hl-1-51">51</a> </span><span class="lnt" id="hl-1-52"><a class="lnlinks" href="#hl-1-52">52</a> </span><span class="lnt" id="hl-1-53"><a class="lnlinks" href="#hl-1-53">53</a> </span><span class="lnt" id="hl-1-54"><a class="lnlinks" href="#hl-1-54">54</a> </span><span class="lnt" id="hl-1-55"><a class="lnlinks" href="#hl-1-55">55</a> </span><span class="lnt" id="hl-1-56"><a class="lnlinks" href="#hl-1-56">56</a> </span><span class="lnt" id="hl-1-57"><a class="lnlinks" href="#hl-1-57">57</a> </span><span class="lnt" id="hl-1-58"><a class="lnlinks" href="#hl-1-58">58</a> </span><span class="lnt" id="hl-1-59"><a class="lnlinks" href="#hl-1-59">59</a> </span><span class="lnt" id="hl-1-60"><a class="lnlinks" href="#hl-1-60">60</a> </span><span class="lnt" id="hl-1-61"><a class="lnlinks" href="#hl-1-61">61</a> </span><span class="lnt" id="hl-1-62"><a class="lnlinks" href="#hl-1-62">62</a> </span><span class="lnt" id="hl-1-63"><a class="lnlinks" href="#hl-1-63">63</a> </span><span class="lnt" id="hl-1-64"><a class="lnlinks" href="#hl-1-64">64</a> </span><span class="lnt" id="hl-1-65"><a class="lnlinks" href="#hl-1-65">65</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">CustomConditionalAuthenticatorFactory</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">ConditionalAuthenticationFactory</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">PROVIDER_ID</span><span class="o">=</span><span class="w"> </span><span class="s">&#34;custom-authenticator&#34;</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">CONFIG_CONDITION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;foo&#34;</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">AuthenticationExecutionModel</span><span class="p">.</span><span class="na">Requirement</span><span class="o">[]</span><span class="w"> </span><span class="n">REQUIREMENT_CHOICES</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">AuthenticationExecutionModel</span><span class="p">.</span><span class="na">Requirement</span><span class="p">.</span><span class="na">REQUIRED</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">AuthenticationExecutionModel</span><span class="p">.</span><span class="na">Requirement</span><span class="p">.</span><span class="na">DISABLED</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">};</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">init</span><span class="p">(</span><span class="n">Config</span><span class="p">.</span><span class="na">Scope</span><span class="w"> </span><span class="n">scope</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">postInit</span><span class="p">(</span><span class="n">KeycloakSessionFactory</span><span class="w"> </span><span class="n">keycloakSessionFactory</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">close</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getId</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">PROVIDER_ID</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getDisplayType</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Condition - Custom Authenticator&#34;</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">isConfigurable</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">AuthenticationExecutionModel</span><span class="p">.</span><span class="na">Requirement</span><span class="o">[]</span><span class="w"> </span><span class="nf">getRequirementChoices</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">REQUIREMENT_CHOICES</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">isUserSetupAllowed</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getHelpText</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Flow is executed if condition is validated.&#34;</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">ProviderConfigProperty</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">getConfigProperties</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">ProviderConfigProperty</span><span class="w"> </span><span class="n">condition</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ProviderConfigProperty</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">condition</span><span class="p">.</span><span class="na">setName</span><span class="p">(</span><span class="n">CONFIG_CONDITION</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">condition</span><span class="p">.</span><span class="na">setLabel</span><span class="p">(</span><span class="s">&#34;Custom Condition&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">condition</span><span class="p">.</span><span class="na">setHelpText</span><span class="p">(</span><span class="s">&#34;Set a value to be validated&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">condition</span><span class="p">.</span><span class="na">setType</span><span class="p">(</span><span class="n">STRING_TYPE</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">condition</span><span class="p">.</span><span class="na">setDefaultValue</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">ConditionalAuthenticator</span><span class="w"> </span><span class="nf">getSingleton</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">CustomConditionalAuthenticator</span><span class="p">.</span><span class="na">SINGLETON</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Nombre de méthodes ne sont pas utilisées pour un authenticator conditionnel. Notre factory est en charge de l&rsquo;instanciation du provider et de la gestion de la configuration dans l&rsquo;interface de Keycloak. Dans notre cas, nous avons une condition de type <code>String</code> ayant pour nom <code>foo</code>. Notre factory définie aussi le type d&rsquo;exécution possible. Ici, nous laisser le choix pour l&rsquo;état <code>Disabled</code> ou <code>Required</code>. La méthode <code>getConfigurationProvider</code> est utilisée pour permettre la configuration du provider depuis l&rsquo;interface d&rsquo;administration de Keycloak.</p> <h2 class="heading-element" id="création-du-provider"><span>Création du provider</span> <a href="#cr%c3%a9ation-du-provider" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Le provider est la classe contenant la logique de notre implémentation.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a> </span><span class="lnt" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a> </span><span class="lnt" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a> </span><span class="lnt" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a> </span><span class="lnt" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a> </span><span class="lnt" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a> </span><span class="lnt" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a> </span><span class="lnt" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a> </span><span class="lnt" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a> </span><span class="lnt" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a> </span><span class="lnt" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a> </span><span class="lnt" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a> </span><span class="lnt" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a> </span><span class="lnt" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a> </span><span class="lnt" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a> </span><span class="lnt" id="hl-2-16"><a class="lnlinks" href="#hl-2-16">16</a> </span><span class="lnt" id="hl-2-17"><a class="lnlinks" href="#hl-2-17">17</a> </span><span class="lnt" id="hl-2-18"><a class="lnlinks" href="#hl-2-18">18</a> </span><span class="lnt" id="hl-2-19"><a class="lnlinks" href="#hl-2-19">19</a> </span><span class="lnt" id="hl-2-20"><a class="lnlinks" href="#hl-2-20">20</a> </span><span class="lnt" id="hl-2-21"><a class="lnlinks" href="#hl-2-21">21</a> </span><span class="lnt" id="hl-2-22"><a class="lnlinks" href="#hl-2-22">22</a> </span><span class="lnt" id="hl-2-23"><a class="lnlinks" href="#hl-2-23">23</a> </span><span class="lnt" id="hl-2-24"><a class="lnlinks" href="#hl-2-24">24</a> </span><span class="lnt" id="hl-2-25"><a class="lnlinks" href="#hl-2-25">25</a> </span><span class="lnt" id="hl-2-26"><a class="lnlinks" href="#hl-2-26">26</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">CustonContidionalAuthenticator</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">ConditionalAuthenticator</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Logger</span><span class="w"> </span><span class="n">logger</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Logger</span><span class="p">.</span><span class="na">getLogger</span><span class="p">(</span><span class="n">ThreatmetrixConditionalAuthenticator</span><span class="p">.</span><span class="na">class</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">matchCondition</span><span class="p">(</span><span class="n">AuthenticationFlowContext</span><span class="w"> </span><span class="n">context</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">config</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">context</span><span class="p">.</span><span class="na">getAuthenticatorConfig</span><span class="p">().</span><span class="na">getConfig</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">config</span><span class="p">.</span><span class="na">containsKey</span><span class="p">(</span><span class="n">CustomConditionalAuthenticatorFactory</span><span class="p">.</span><span class="na">CONFIG_CONDITION</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">AuthenticationFlowException</span><span class="p">(</span><span class="n">AuthenticationFlowError</span><span class="p">.</span><span class="na">valueOf</span><span class="p">(</span><span class="s">&#34;Missing url for Threatmetrix configuration.&#34;</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">condition</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">config</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">CustomConditionalAuthenticatorFactory</span><span class="p">.</span><span class="na">CONFIG_CONDITION</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">evaluate</span><span class="p">(</span><span class="n">condition</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">action</span><span class="p">(</span><span class="n">AuthenticationFlowContext</span><span class="w"> </span><span class="n">authenticationFlowContext</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">requiresUser</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setRequiredActions</span><span class="p">(</span><span class="n">KeycloakSession</span><span class="w"> </span><span class="n">keycloakSession</span><span class="p">,</span><span class="w"> </span><span class="n">RealmModel</span><span class="w"> </span><span class="n">realmModel</span><span class="p">,</span><span class="w"> </span><span class="n">UserModel</span><span class="w"> </span><span class="n">userModel</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">close</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span></span></span></code></pre></td></tr></table> </div> </div><p><code>matchCondition</code> est la seule méthode utilisée. Elle permet d&rsquo;évaluer si la condition sera positive ou négative. Pour simplifier le code, notre implémentation appelle la méthode <code>evaluate</code> en charge d&rsquo;évaluer notre condition. C&rsquo;est ici que sera définir le traitement souhaité pour l&rsquo;implémentation.</p> <h2 class="heading-element" id="référencement-de-notre-provider-dans-keycoak"><span>Référencement de notre provider dans Keycoak</span> <a href="#r%c3%a9f%c3%a9rencement-de-notre-provider-dans-keycoak" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Il ne nous reste plus qu&rsquo;à référencer notre provider dans Keycloak en créant le fichier <code>resources/META-INF/services/org.keycloak.authentication.AuthenticationFactory</code> avec le FQDN de notre factory.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a class="lnlinks" href="#hl-3-1">1</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">org.techland.keycloak.provider.CustomConditionalAuthenticatorFactory</span></span></code></pre></td></tr></table> </div> </div><h2 class="heading-element" id="conclusion"><span>Conclusion</span> <a href="#conclusion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Le création d&rsquo;un authenticator personnalisé est simple et ne demande que l&rsquo;implémentation de deux classes, la factory et le provider. Une fois le <code>jar</code> créé, il suffit de le déposer dans le dossier <code>/opt/keycloak/providers</code> puis de se rendre dans le menu <code>Authentication</code> et sélectionner un flow personnalisé pour ajouter notre nouvelle condition.</p> Injection de token oauth2 dans des tests JUnit https://techland.info/posts/java/8a8ec78/ Thu, 19 Jun 2025 17:23:12 +0200[email protected] (LE BARO Romain) https://techland.info/posts/java/8a8ec78/ Développement <p>Lorsque l&rsquo;on développe des tests API, il arrive fréquemment de devoir s&rsquo;authentifier pour effectuer l&rsquo;appel. Réécrire le code permettant de récupérer un token valide est non seulement fastidieux, source d&rsquo;erreur, mais également une mauvaise pratique en termes de maintenabilité. Nous allons voir comment injecter un token valide directement en paramètre de test JUnit de manière à pouvoir l&rsquo;utiliser simplement là où cela est nécessaire.</p> <h2 class="heading-element" id="introduction"><span>Introduction</span> <a href="#introduction" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Une solution couramment mise en place pour répondre à cette problématique est de créer une méthode utilitaire permettant de récupérer le token suivant le flow de connexion supporté par l&rsquo;application. Cette méthode sera probablement définie dans une classe <code>utils</code> ou <code>common</code>, noyée au milieu de nombreuses autres méthodes. Cette classe étant elle-même sûrement dans un package <code>util</code> totalement générique. Les problématiques sont très bien expliqué dans <a href="https://mattilehtinen.com/articles/dunghill-anti-pattern-why-utility-classes-and-modules-smell/"target="_blank" rel="external nofollow noopener noreferrer">cet article</a>. Je vous propose donc de voir comment mettre en œuvre le mécanisme d&rsquo;extension JUnit pour réaliser proprement une injection de token dans chaque test, le nécessitant.</p> <h2 class="heading-element" id="junit-extension"><span>JUnit Extension</span> <a href="#junit-extension" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Les extensions JUnit est une fonctionnalité apparue avec JUnit 5 permettant de modifier le comportant de JUnit lors de l&rsquo;exécution des tests. La <a href="https://junit.org/junit5/docs/current/user-guide/#extensions"target="_blank" rel="external nofollow noopener noreferrer">documentation</a> est très bien faite, je vous invite à la lire. Une extension s&rsquo;utilise via une annotation spéciale <code>@ExtendWith</code> sur une classe de test, un champ de classe, un paramètre de constructeur, dans une méthode de tests ou sur les méthodes annotées <code>@BeforeAll</code>, <code>@AfterAll</code>, <code>@BeforeEach</code>, and <code>@AfterEach</code>.</p> <p>Exemple pour une classe :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@ExtendWith</span><span class="p">(</span><span class="n">MyExtension</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">MyTestClass</span><span class="w"> </span><span class="p">{}</span></span></span></code></pre></td></tr></table> </div> </div><p>Ou un test en particulier</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a> </span><span class="lnt" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a> </span><span class="lnt" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a> </span><span class="lnt" id="hl-1-4"><a class="lnlinks" href="#hl-1-4">4</a> </span><span class="lnt" id="hl-1-5"><a class="lnlinks" href="#hl-1-5">5</a> </span><span class="lnt" id="hl-1-6"><a class="lnlinks" href="#hl-1-6">6</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">MyTestClass</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Test</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@ExtendWith</span><span class="p">(</span><span class="n">MyExtension</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">myTest</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><h2 class="heading-element" id="bearerparameterextension"><span>BearerParameterExtension</span> <a href="#bearerparameterextension" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Il existe plusieurs types d&rsquo;extensions proposées par JUnit. Nous allons utiliser une extension implémentant l&rsquo;interface <code>ParameterResolver</code>. Cette interface définie une extension permettant d&rsquo;injecter une valeur lorsque qu&rsquo;un test demande un paramètre spécifique dans la signature de sa méthode. Dans notre cas, notre test va demander un token en paramètre.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a> </span><span class="lnt" id="hl-2-2"><a class="lnlinks" href="#hl-2-2">2</a> </span><span class="lnt" id="hl-2-3"><a class="lnlinks" href="#hl-2-3">3</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">void</span><span class="w"> </span><span class="nf">MyTest</span><span class="p">(</span><span class="n">Token</span><span class="w"> </span><span class="n">token</span><span class="p">){</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Un objet <code>Token</code> est passé ici à la place d&rsquo;un simple <code>String</code> afin de permettre à notre extension de différencier notre token de n&rsquo;importe quel autre paramètre de type <code>String</code>.</p> <p>Voici maintenant le code de l&rsquo;extension :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a class="lnlinks" href="#hl-3-1"> 1</a> </span><span class="lnt" id="hl-3-2"><a class="lnlinks" href="#hl-3-2"> 2</a> </span><span class="lnt" id="hl-3-3"><a class="lnlinks" href="#hl-3-3"> 3</a> </span><span class="lnt" id="hl-3-4"><a class="lnlinks" href="#hl-3-4"> 4</a> </span><span class="lnt" id="hl-3-5"><a class="lnlinks" href="#hl-3-5"> 5</a> </span><span class="lnt" id="hl-3-6"><a class="lnlinks" href="#hl-3-6"> 6</a> </span><span class="lnt" id="hl-3-7"><a class="lnlinks" href="#hl-3-7"> 7</a> </span><span class="lnt" id="hl-3-8"><a class="lnlinks" href="#hl-3-8"> 8</a> </span><span class="lnt" id="hl-3-9"><a class="lnlinks" href="#hl-3-9"> 9</a> </span><span class="lnt" id="hl-3-10"><a class="lnlinks" href="#hl-3-10">10</a> </span><span class="lnt" id="hl-3-11"><a class="lnlinks" href="#hl-3-11">11</a> </span><span class="lnt" id="hl-3-12"><a class="lnlinks" href="#hl-3-12">12</a> </span><span class="lnt" id="hl-3-13"><a class="lnlinks" href="#hl-3-13">13</a> </span><span class="lnt" id="hl-3-14"><a class="lnlinks" href="#hl-3-14">14</a> </span><span class="lnt" id="hl-3-15"><a class="lnlinks" href="#hl-3-15">15</a> </span><span class="lnt" id="hl-3-16"><a class="lnlinks" href="#hl-3-16">16</a> </span><span class="lnt" id="hl-3-17"><a class="lnlinks" href="#hl-3-17">17</a> </span><span class="lnt" id="hl-3-18"><a class="lnlinks" href="#hl-3-18">18</a> </span><span class="lnt" id="hl-3-19"><a class="lnlinks" href="#hl-3-19">19</a> </span><span class="lnt" id="hl-3-20"><a class="lnlinks" href="#hl-3-20">20</a> </span><span class="lnt" id="hl-3-21"><a class="lnlinks" href="#hl-3-21">21</a> </span><span class="lnt" id="hl-3-22"><a class="lnlinks" href="#hl-3-22">22</a> </span><span class="lnt" id="hl-3-23"><a class="lnlinks" href="#hl-3-23">23</a> </span><span class="lnt" id="hl-3-24"><a class="lnlinks" href="#hl-3-24">24</a> </span><span class="lnt" id="hl-3-25"><a class="lnlinks" href="#hl-3-25">25</a> </span><span class="lnt" id="hl-3-26"><a class="lnlinks" href="#hl-3-26">26</a> </span><span class="lnt" id="hl-3-27"><a class="lnlinks" href="#hl-3-27">27</a> </span><span class="lnt" id="hl-3-28"><a class="lnlinks" href="#hl-3-28">28</a> </span><span class="lnt" id="hl-3-29"><a class="lnlinks" href="#hl-3-29">29</a> </span><span class="lnt" id="hl-3-30"><a class="lnlinks" href="#hl-3-30">30</a> </span><span class="lnt" id="hl-3-31"><a class="lnlinks" href="#hl-3-31">31</a> </span><span class="lnt" id="hl-3-32"><a class="lnlinks" href="#hl-3-32">32</a> </span><span class="lnt" id="hl-3-33"><a class="lnlinks" href="#hl-3-33">33</a> </span><span class="lnt" id="hl-3-34"><a class="lnlinks" href="#hl-3-34">34</a> </span><span class="lnt" id="hl-3-35"><a class="lnlinks" href="#hl-3-35">35</a> </span><span class="lnt" id="hl-3-36"><a class="lnlinks" href="#hl-3-36">36</a> </span><span class="lnt" id="hl-3-37"><a class="lnlinks" href="#hl-3-37">37</a> </span><span class="lnt" id="hl-3-38"><a class="lnlinks" href="#hl-3-38">38</a> </span><span class="lnt" id="hl-3-39"><a class="lnlinks" href="#hl-3-39">39</a> </span><span class="lnt" id="hl-3-40"><a class="lnlinks" href="#hl-3-40">40</a> </span><span class="lnt" id="hl-3-41"><a class="lnlinks" href="#hl-3-41">41</a> </span><span class="lnt" id="hl-3-42"><a class="lnlinks" href="#hl-3-42">42</a> </span><span class="lnt" id="hl-3-43"><a class="lnlinks" href="#hl-3-43">43</a> </span><span class="lnt" id="hl-3-44"><a class="lnlinks" href="#hl-3-44">44</a> </span><span class="lnt" id="hl-3-45"><a class="lnlinks" href="#hl-3-45">45</a> </span><span class="lnt" id="hl-3-46"><a class="lnlinks" href="#hl-3-46">46</a> </span><span class="lnt" id="hl-3-47"><a class="lnlinks" href="#hl-3-47">47</a> </span><span class="lnt" id="hl-3-48"><a class="lnlinks" href="#hl-3-48">48</a> </span><span class="lnt" id="hl-3-49"><a class="lnlinks" href="#hl-3-49">49</a> </span><span class="lnt" id="hl-3-50"><a class="lnlinks" href="#hl-3-50">50</a> </span><span class="lnt" id="hl-3-51"><a class="lnlinks" href="#hl-3-51">51</a> </span><span class="lnt" id="hl-3-52"><a class="lnlinks" href="#hl-3-52">52</a> </span><span class="lnt" id="hl-3-53"><a class="lnlinks" href="#hl-3-53">53</a> </span><span class="lnt" id="hl-3-54"><a class="lnlinks" href="#hl-3-54">54</a> </span><span class="lnt" id="hl-3-55"><a class="lnlinks" href="#hl-3-55">55</a> </span><span class="lnt" id="hl-3-56"><a class="lnlinks" href="#hl-3-56">56</a> </span><span class="lnt" id="hl-3-57"><a class="lnlinks" href="#hl-3-57">57</a> </span><span class="lnt" id="hl-3-58"><a class="lnlinks" href="#hl-3-58">58</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.io.IOException</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.net.URI</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.net.URLEncoder</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.net.http.HttpClient</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.net.http.HttpRequest</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.net.http.HttpResponse</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.nio.charset.StandardCharset</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.extension.ExtensionContext</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.extension.ParameterContext</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.extension.ParameterResolutionException</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.extension.ParameterResolver</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">com.fasterxml.jackson.databind.ObjectMapper</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">BearerParameterResolver</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">ParameterResolver</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Overrride</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">supportsParameter</span><span class="p">(</span><span class="n">ParameterContext</span><span class="w"> </span><span class="n">parameterContext</span><span class="p">,</span><span class="w"> </span><span class="n">ExtensionContext</span><span class="w"> </span><span class="n">extensionContext</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">ParameterResolutionException</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">parameterContext</span><span class="p">.</span><span class="na">getParameter</span><span class="p">().</span><span class="na">getType</span><span class="p">().</span><span class="na">equals</span><span class="p">(</span><span class="n">Token</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Object</span><span class="w"> </span><span class="nf">resolverParameter</span><span class="p">(</span><span class="n">ParameterContext</span><span class="w"> </span><span class="n">parameterContext</span><span class="p">,</span><span class="w"> </span><span class="n">ExtensionContext</span><span class="w"> </span><span class="n">extensionContext</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">ParameterResolutionException</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">parameterContext</span><span class="p">.</span><span class="na">findAnnotation</span><span class="p">(</span><span class="n">ClientCredential</span><span class="p">.</span><span class="na">class</span><span class="p">).</span><span class="na">map</span><span class="p">(</span><span class="n">annotation</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">realm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">annotation</span><span class="p">.</span><span class="na">realm</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">clientId</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">annotation</span><span class="p">.</span><span class="na">clientId</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">clientSecret</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">annotation</span><span class="p">.</span><span class="na">clientSecret</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">authenticate</span><span class="p">(</span><span class="n">realm</span><span class="p">,</span><span class="w"> </span><span class="n">clientId</span><span class="p">,</span><span class="w"> </span><span class="n">clientSecret</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Token</span><span class="p">(</span><span class="n">token</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">IOException</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">InterruptedException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ParameterResolutionException</span><span class="p">(</span><span class="s">&#34;Failed to authenticate with Keycloak&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">e</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">})</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">authenticate</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">realm</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">cleintId</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">clientSecret</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">IOException</span><span class="p">,</span><span class="w"> </span><span class="n">InterruptionException</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">tokenUrl</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Env</span><span class="p">.</span><span class="na">KEYCLOAK_URL</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;/realms/&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">realm</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;/protocol/openid-connect/token&#34;</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;grant_type=client_credentials&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;&amp;client_id=&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">URLEncoder</span><span class="p">.</span><span class="na">encode</span><span class="p">(</span><span class="n">clientId</span><span class="p">,</span><span class="w"> </span><span class="n">StandardCharset</span><span class="p">.</span><span class="na">UTF_8</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;&amp;client_secret=&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">URLEncoder</span><span class="p">.</span><span class="na">encode</span><span class="p">(</span><span class="n">clientSecret</span><span class="p">,</span><span class="w"> </span><span class="n">StandardCharset</span><span class="p">.</span><span class="na">UTF_8</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">HttpRequest</span><span class="w"> </span><span class="n">request</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">HttpRequest</span><span class="p">.</span><span class="na">newBuilder</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">uri</span><span class="p">(</span><span class="n">URI</span><span class="p">.</span><span class="na">create</span><span class="p">(</span><span class="n">tokenUrl</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">header</span><span class="p">(</span><span class="s">&#34;Content-Type&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;application/x-www-form-urlencoded&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">POST</span><span class="p">(</span><span class="n">HttpRequest</span><span class="p">.</span><span class="na">BodyPublishers</span><span class="p">.</span><span class="na">ofString</span><span class="p">(</span><span class="n">body</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">build</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">HttpClient</span><span class="w"> </span><span class="n">client</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">HttpClient</span><span class="p">.</span><span class="na">newHttpClient</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">HttpResponse</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">client</span><span class="p">.</span><span class="na">send</span><span class="p">(</span><span class="n">request</span><span class="p">,</span><span class="w"> </span><span class="n">HttpResponse</span><span class="p">.</span><span class="na">BodyHandlers</span><span class="p">.</span><span class="na">ofString</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">ObjectMapper</span><span class="w"> </span><span class="n">mapper</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ObjectMapper</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">TokenResponse</span><span class="w"> </span><span class="n">tokenResponse</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">objectMapper</span><span class="p">.</span><span class="na">readValue</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="na">body</span><span class="p">(),</span><span class="w"> </span><span class="n">TokenResponse</span><span class="p">.</span><span class="na">class</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">tokenResponse</span><span class="p">.</span><span class="na">access_token</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Plusieurs points à préciser ici :</p> <ul> <li>Notre extension utilise une annotation à appliquer sur le paramètre du test pour configurer l&rsquo;injection.</li> <li>L&rsquo;authorization provider utilisé ici est <code>Keycloak</code>. J&rsquo;utilise donc le concept de <code>realm</code> pour reconstruire dynamiquement l&rsquo;URL du token provider dans la méthode <code>authenticate</code>. Une implémentation plus générique demanderait l&rsquo;URL du token endpoint mais je préfère garder l&rsquo;annotation la plus concise possible.</li> <li>La méthode <code>authenticate</code> va faire l&rsquo;authentification et récupérer le token. C&rsquo;est l&rsquo;équivalent de notre méthode utilitaire dont je parlais en début d&rsquo;article.</li> </ul> <h2 class="heading-element" id="utilisation-de-lextension"><span>Utilisation de l&rsquo;extension</span> <a href="#utilisation-de-lextension" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>En reprenant l&rsquo;exemple du test ci-dessus, nous pouvons utiliser l&rsquo;extension de la manière suivante :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a class="lnlinks" href="#hl-4-1"> 1</a> </span><span class="lnt" id="hl-4-2"><a class="lnlinks" href="#hl-4-2"> 2</a> </span><span class="lnt" id="hl-4-3"><a class="lnlinks" href="#hl-4-3"> 3</a> </span><span class="lnt" id="hl-4-4"><a class="lnlinks" href="#hl-4-4"> 4</a> </span><span class="lnt" id="hl-4-5"><a class="lnlinks" href="#hl-4-5"> 5</a> </span><span class="lnt" id="hl-4-6"><a class="lnlinks" href="#hl-4-6"> 6</a> </span><span class="lnt" id="hl-4-7"><a class="lnlinks" href="#hl-4-7"> 7</a> </span><span class="lnt" id="hl-4-8"><a class="lnlinks" href="#hl-4-8"> 8</a> </span><span class="lnt" id="hl-4-9"><a class="lnlinks" href="#hl-4-9"> 9</a> </span><span class="lnt" id="hl-4-10"><a class="lnlinks" href="#hl-4-10">10</a> </span><span class="lnt" id="hl-4-11"><a class="lnlinks" href="#hl-4-11">11</a> </span><span class="lnt" id="hl-4-12"><a class="lnlinks" href="#hl-4-12">12</a> </span><span class="lnt" id="hl-4-13"><a class="lnlinks" href="#hl-4-13">13</a> </span><span class="lnt" id="hl-4-14"><a class="lnlinks" href="#hl-4-14">14</a> </span><span class="lnt" id="hl-4-15"><a class="lnlinks" href="#hl-4-15">15</a> </span><span class="lnt" id="hl-4-16"><a class="lnlinks" href="#hl-4-16">16</a> </span><span class="lnt" id="hl-4-17"><a class="lnlinks" href="#hl-4-17">17</a> </span><span class="lnt" id="hl-4-18"><a class="lnlinks" href="#hl-4-18">18</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">import static</span><span class="w"> </span><span class="nn">io.restassured.RestAssured.*</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@ExtendWith</span><span class="p">(</span><span class="n">BearerParameterResolver</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">MyTestClass</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Test</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">myTest</span><span class="p">(</span><span class="nd">@ClientCredential</span><span class="p">(</span><span class="n">clientId</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;myClientId&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">clientSecret</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;mySecret&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">realm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;myRealm&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">Token</span><span class="w"> </span><span class="n">token</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">given</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">body</span><span class="p">(</span><span class="s">&#34;...&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">contentType</span><span class="p">(</span><span class="s">&#34;application/json&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">auth</span><span class="p">().</span><span class="na">preemptive</span><span class="p">().</span><span class="na">oauth2</span><span class="p">(</span><span class="n">token</span><span class="p">.</span><span class="na">getValue</span><span class="p">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">when</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">post</span><span class="p">(</span><span class="s">&#34;...&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">then</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">statusCode</span><span class="p">(</span><span class="n">200</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Nous pouvons donc maintenant simplement injecter des tokens valides dans nos tests. Ici, nous l&rsquo;utilisons via <code>restAssured</code> pour effectuer une requête <code>POST</code> sur notre API en étant authentifié.</p> <h2 class="heading-element" id="point-à-améliorer"><span>Point à améliorer</span> <a href="#point-%c3%a0-am%c3%a9liorer" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Le code n&rsquo;est pas parfait. Voici quelques points à améliorer selon moi :</p> <ul> <li>Prendre en compte, plusieurs flow de connection via différentes annotations (<code>@ClientCredentials</code>, <code>@DirectGrant</code>, <code>@AuthorizationCode</code>&hellip;)</li> <li>Remplacer la classe Env par un chargement des variables via fichiers de configuration par environnement</li> <li>Dans le test, trouver un autre moyen que de passer en dur le secret à l&rsquo;annotation</li> </ul> <h2 class="heading-element" id="conclusion"><span>Conclusion</span> <a href="#conclusion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Le système d&rsquo;extension de JUnit permet d&rsquo;injecter facilement des objets en paramètre des tests. L&rsquo;injection de dépendance permet de découpler la logique de préparation des données nécessaires à l&rsquo;exécution du test de celle purement fonctionnelle. Cela apporte une plus grande lisibilité et une maintenance facilitée. D&rsquo;autres extensions existent avec JUnit pour, par exemple agir sur le cycle de vie des tests et permettre de démarrer automatiquement des conteneurs docker, importer des données dans une base… .</p> <h2 class="heading-element" id="annexe"><span>Annexe</span> <a href="#annexe" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Pour référence voici les classes <code>Token</code> et `TokenResponse utiliser dans le code ci-dessus :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a class="lnlinks" href="#hl-5-1"> 1</a> </span><span class="lnt" id="hl-5-2"><a class="lnlinks" href="#hl-5-2"> 2</a> </span><span class="lnt" id="hl-5-3"><a class="lnlinks" href="#hl-5-3"> 3</a> </span><span class="lnt" id="hl-5-4"><a class="lnlinks" href="#hl-5-4"> 4</a> </span><span class="lnt" id="hl-5-5"><a class="lnlinks" href="#hl-5-5"> 5</a> </span><span class="lnt" id="hl-5-6"><a class="lnlinks" href="#hl-5-6"> 6</a> </span><span class="lnt" id="hl-5-7"><a class="lnlinks" href="#hl-5-7"> 7</a> </span><span class="lnt" id="hl-5-8"><a class="lnlinks" href="#hl-5-8"> 8</a> </span><span class="lnt" id="hl-5-9"><a class="lnlinks" href="#hl-5-9"> 9</a> </span><span class="lnt" id="hl-5-10"><a class="lnlinks" href="#hl-5-10">10</a> </span><span class="lnt" id="hl-5-11"><a class="lnlinks" href="#hl-5-11">11</a> </span><span class="lnt" id="hl-5-12"><a class="lnlinks" href="#hl-5-12">12</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Token</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">value</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">Token</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">token</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">value</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getValue</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">value</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a class="lnlinks" href="#hl-6-1"> 1</a> </span><span class="lnt" id="hl-6-2"><a class="lnlinks" href="#hl-6-2"> 2</a> </span><span class="lnt" id="hl-6-3"><a class="lnlinks" href="#hl-6-3"> 3</a> </span><span class="lnt" id="hl-6-4"><a class="lnlinks" href="#hl-6-4"> 4</a> </span><span class="lnt" id="hl-6-5"><a class="lnlinks" href="#hl-6-5"> 5</a> </span><span class="lnt" id="hl-6-6"><a class="lnlinks" href="#hl-6-6"> 6</a> </span><span class="lnt" id="hl-6-7"><a class="lnlinks" href="#hl-6-7"> 7</a> </span><span class="lnt" id="hl-6-8"><a class="lnlinks" href="#hl-6-8"> 8</a> </span><span class="lnt" id="hl-6-9"><a class="lnlinks" href="#hl-6-9"> 9</a> </span><span class="lnt" id="hl-6-10"><a class="lnlinks" href="#hl-6-10">10</a> </span><span class="lnt" id="hl-6-11"><a class="lnlinks" href="#hl-6-11">11</a> </span><span class="lnt" id="hl-6-12"><a class="lnlinks" href="#hl-6-12">12</a> </span><span class="lnt" id="hl-6-13"><a class="lnlinks" href="#hl-6-13">13</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">TokenResponse</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@JsonProperty</span><span class="p">(</span><span class="s">&#34;access_token&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">accessToken</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@JsonProperty</span><span class="p">(</span><span class="s">&#34;expires_in&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">expiresIn</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@JsonProperty</span><span class="p">(</span><span class="s">&#34;refresh_expires_in&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">refreshExpiresIn</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@JsonProperty</span><span class="p">(</span><span class="s">&#34;token_type&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">tokenType</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@JsonProperty</span><span class="p">(</span><span class="s">&#34;not-before-policy&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">not_before_policy</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">scope</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> Quarkus Extension Dev Service Personnalisé - Partie III https://techland.info/posts/quarkus/quarkus-extension-custom-dev-service-part-3/ Fri, 11 Oct 2024 15:07:03 +0200[email protected] (LE BARO Romain) https://techland.info/posts/quarkus/quarkus-extension-custom-dev-service-part-3/ Développement <p>Dans le <a href="https://techland.info/posts/quarkus/quarkus-extension-custom-dev-service-part-2/">précédent article</a>, nous avons vu comment lancer automatiquement un conteneur exposant une API et relié à sa propre base de données grâce à une extension Quarkus. Nous allons maintenant voir comment communiquer avec cette api depuis notre application principale.</p> <h2 class="heading-element" id="pour-appeler-une-api-il-nous-faut-son-url-"><span>Pour appeler une API, il nous faut son URL !</span> <a href="#pour-appeler-une-api-il-nous-faut-son-url-" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Dans le précédent article, nous avions sauvegardé l&rsquo;URL d&rsquo;accès à notre API dans un <code>RunningDevService</code>. Nous allons maintenant utiliser cette URL pour permettre d&rsquo;injecter cette valeur dans notre application principale. Pour ce faire, nous allons créer une nouvelle classe nommée <code>DemoClientApiProcessor</code> :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a> </span><span class="lnt" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a> </span><span class="lnt" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a> </span><span class="lnt" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a> </span><span class="lnt" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a> </span><span class="lnt" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a> </span><span class="lnt" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a> </span><span class="lnt" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a> </span><span class="lnt" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a> </span><span class="lnt" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a> </span><span class="lnt" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a> </span><span class="lnt" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a> </span><span class="lnt" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a> </span><span class="lnt" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a> </span><span class="lnt" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a> </span><span class="lnt" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a> </span><span class="lnt" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a> </span><span class="lnt" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a> </span><span class="lnt" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a> </span><span class="lnt" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a> </span><span class="lnt" id="hl-0-22"><a class="lnlinks" href="#hl-0-22">22</a> </span><span class="lnt" id="hl-0-23"><a class="lnlinks" href="#hl-0-23">23</a> </span><span class="lnt" id="hl-0-24"><a class="lnlinks" href="#hl-0-24">24</a> </span><span class="lnt" id="hl-0-25"><a class="lnlinks" href="#hl-0-25">25</a> </span><span class="lnt" id="hl-0-26"><a class="lnlinks" href="#hl-0-26">26</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@BuildSteps</span><span class="p">(</span><span class="n">onlyIf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">GlobalDevServicesConfig</span><span class="p">.</span><span class="na">Enabled</span><span class="p">.</span><span class="na">class</span><span class="w"> </span><span class="p">})</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoClientApiProcessor</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Record</span><span class="p">(</span><span class="n">ExecutionTime</span><span class="p">.</span><span class="na">RUNTIME_INIT</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildStep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">createSDK</span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">DevServicesResultBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">devServicesResultBuildItem</span><span class="p">,</span><span class="w"> </span><span class="n">DemoServiceRecorder</span><span class="w"> </span><span class="n">recorder</span><span class="p">,</span><span class="w"> </span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">SyntheticBeanBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">syntheticBeanBuildItemBuildProducer</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Retrieve config set Inside DemoExtensionProcessor</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">apiUrl</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">devServicesResultBuildItem</span><span class="p">.</span><span class="na">stream</span><span class="p">().</span><span class="na">filter</span><span class="p">(</span><span class="n">devService</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">devService</span><span class="p">.</span><span class="na">getName</span><span class="p">().</span><span class="na">equals</span><span class="p">(</span><span class="n">DemoServiceContainer</span><span class="p">.</span><span class="na">CONTAINER_NAME</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">findFirst</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">orElseThrow</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalStateException</span><span class="p">(</span><span class="s">&#34;Can&#39;t find Demo-service url&#34;</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">getConfig</span><span class="p">().</span><span class="na">get</span><span class="p">(</span><span class="s">&#34;url&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">syntheticBeanBuildItemBuildProducer</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="n">SyntheticBeanBuildItem</span><span class="p">.</span><span class="na">configure</span><span class="p">(</span><span class="n">DemoConfig</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">unremovable</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">setRuntimeInit</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">runtimeValue</span><span class="p">(</span><span class="n">recorder</span><span class="p">.</span><span class="na">createConfig</span><span class="p">(</span><span class="s">&#34;http://&#34;</span><span class="o">+</span><span class="n">apiUrl</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">done</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildStep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">addSDKBeans</span><span class="p">(</span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">AdditionalBeanBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">additionalBeans</span><span class="p">,</span><span class="w"> </span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">AdditionalIndexedClassesBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">additionalIndexedClasses</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">additionalBeans</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">AdditionalBeanBuildItem</span><span class="p">(</span><span class="n">DemoService</span><span class="p">.</span><span class="na">class</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">additionalIndexedClasses</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">AdditionalIndexedClassesBuildItem</span><span class="p">(</span><span class="n">DemoService</span><span class="p">.</span><span class="na">class</span><span class="p">.</span><span class="na">getName</span><span class="p">()));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>La méthode createSDK annotée avec <code>@BuildStep</code>, prend en paramètre une liste de <code>DevServicesResultBuildItem</code>. Quarkus appellera donc cette méthode après l&rsquo;instanciation de nos conteneurs de base de données et d&rsquo;API. Les autres paramètres sont un <code>DemoServiceRecorder</code> et un <code>BuildProducer</code>de <code>SyntheticBeanBuildItem</code>. Le fonctionnement des recorders et syntheticBean a déjà été couvert dans un <a href="https://techland.info/posts/quarkus/quarkus-extension-synthetic-build-item/">autre article</a>. En résumé, On récupère l&rsquo;URL de l&rsquo;API depuis object <code>devServicesResultBuildItem</code> et on construit un <code>DemoConfig</code> contenant cette URL qui sera injectable.</p> <p>Le <code>recorder</code> est définie comme suit :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a> </span><span class="lnt" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a> </span><span class="lnt" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a> </span><span class="lnt" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a> </span><span class="lnt" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a> </span><span class="lnt" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a> </span><span class="lnt" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a> </span><span class="lnt" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a> </span><span class="lnt" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a> </span><span class="lnt" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a> </span><span class="lnt" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a> </span><span class="lnt" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a> </span><span class="lnt" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a> </span><span class="lnt" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">info.techland.demo.extension.runtime</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.quarkus.runtime.RuntimeValue</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.quarkus.runtime.annotations.Recorder</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@Recorder</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoServiceRecorder</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">RuntimeValue</span><span class="o">&lt;</span><span class="n">DemoConfig</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">createConfig</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RuntimeValue</span><span class="o">&lt;&gt;</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">DemoConfig</span><span class="p">(</span><span class="n">url</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Le recorder est défini dans le package <code>runtime</code> de notre extension. Son rôle est d&rsquo;instancier notre objet <code>DemoConfig</code> avec l&rsquo;URL passé en paramètre de manière que Quarkus puisse enregistrer le Bytecode en résultant.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a> </span><span class="lnt" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a> </span><span class="lnt" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a> </span><span class="lnt" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a> </span><span class="lnt" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a> </span><span class="lnt" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a> </span><span class="lnt" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a> </span><span class="lnt" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a> </span><span class="lnt" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a> </span><span class="lnt" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a> </span><span class="lnt" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a> </span><span class="lnt" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a> </span><span class="lnt" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a> </span><span class="lnt" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a> </span><span class="lnt" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a> </span><span class="lnt" id="hl-2-16"><a class="lnlinks" href="#hl-2-16">16</a> </span><span class="lnt" id="hl-2-17"><a class="lnlinks" href="#hl-2-17">17</a> </span><span class="lnt" id="hl-2-18"><a class="lnlinks" href="#hl-2-18">18</a> </span><span class="lnt" id="hl-2-19"><a class="lnlinks" href="#hl-2-19">19</a> </span><span class="lnt" id="hl-2-20"><a class="lnlinks" href="#hl-2-20">20</a> </span><span class="lnt" id="hl-2-21"><a class="lnlinks" href="#hl-2-21">21</a> </span><span class="lnt" id="hl-2-22"><a class="lnlinks" href="#hl-2-22">22</a> </span><span class="lnt" id="hl-2-23"><a class="lnlinks" href="#hl-2-23">23</a> </span><span class="lnt" id="hl-2-24"><a class="lnlinks" href="#hl-2-24">24</a> </span><span class="lnt" id="hl-2-25"><a class="lnlinks" href="#hl-2-25">25</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">info.techland.demo.extension.runtime</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">jakarta.enterprise.context.ApplicationScoped</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@ApplicationScoped</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoConfig</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Only for CDI injection</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@SuppressWarnings</span><span class="p">(</span><span class="s">&#34;unused&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="nf">DemoConfig</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoConfig</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getUrl</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setUrl</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Notre DemoConfig est juste un Bean contenant l&rsquo;URL de notre API une fois déployé comme devService.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a class="lnlinks" href="#hl-3-1">1</a> </span><span class="lnt" id="hl-3-2"><a class="lnlinks" href="#hl-3-2">2</a> </span><span class="lnt" id="hl-3-3"><a class="lnlinks" href="#hl-3-3">3</a> </span><span class="lnt" id="hl-3-4"><a class="lnlinks" href="#hl-3-4">4</a> </span><span class="lnt" id="hl-3-5"><a class="lnlinks" href="#hl-3-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@BuildStep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">addSDKBeans</span><span class="p">(</span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">AdditionalBeanBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">additionalBeans</span><span class="p">,</span><span class="w"> </span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">AdditionalIndexedClassesBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">additionalIndexedClasses</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">additionalBeans</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">AdditionalBeanBuildItem</span><span class="p">(</span><span class="n">DemoService</span><span class="p">.</span><span class="na">class</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">additionalIndexedClasses</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">AdditionalIndexedClassesBuildItem</span><span class="p">(</span><span class="n">DemoService</span><span class="p">.</span><span class="na">class</span><span class="p">.</span><span class="na">getName</span><span class="p">()));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Cette étape permet de déclarer notre <code>syntheticBean</code> comme injectable dans notre application finale.</p> <h2 class="heading-element" id="construire-un-restclient-dans-notre-application"><span>Construire un RestClient dans notre application.</span> <a href="#construire-un-restclient-dans-notre-application" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Nous avons maintenant une instance de <code>DemoConfig</code> qui est injectable dans n&rsquo;importe quelle classe de notre application principale. Commençons par créer une interface pour notre <code>RestClient</code> dans notre application principale afin d&rsquo;appeler notre API Demo.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a class="lnlinks" href="#hl-4-1">1</a> </span><span class="lnt" id="hl-4-2"><a class="lnlinks" href="#hl-4-2">2</a> </span><span class="lnt" id="hl-4-3"><a class="lnlinks" href="#hl-4-3">3</a> </span><span class="lnt" id="hl-4-4"><a class="lnlinks" href="#hl-4-4">4</a> </span><span class="lnt" id="hl-4-5"><a class="lnlinks" href="#hl-4-5">5</a> </span><span class="lnt" id="hl-4-6"><a class="lnlinks" href="#hl-4-6">6</a> </span><span class="lnt" id="hl-4-7"><a class="lnlinks" href="#hl-4-7">7</a> </span><span class="lnt" id="hl-4-8"><a class="lnlinks" href="#hl-4-8">8</a> </span><span class="lnt" id="hl-4-9"><a class="lnlinks" href="#hl-4-9">9</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">info.techland.demo.app</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@Path</span><span class="p">(</span><span class="s">&#34;/api&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">interface</span> <span class="nc">DemoService</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@GET</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Set</span><span class="o">&lt;</span><span class="n">Demo</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">fetchAll</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a class="lnlinks" href="#hl-5-1"> 1</a> </span><span class="lnt" id="hl-5-2"><a class="lnlinks" href="#hl-5-2"> 2</a> </span><span class="lnt" id="hl-5-3"><a class="lnlinks" href="#hl-5-3"> 3</a> </span><span class="lnt" id="hl-5-4"><a class="lnlinks" href="#hl-5-4"> 4</a> </span><span class="lnt" id="hl-5-5"><a class="lnlinks" href="#hl-5-5"> 5</a> </span><span class="lnt" id="hl-5-6"><a class="lnlinks" href="#hl-5-6"> 6</a> </span><span class="lnt" id="hl-5-7"><a class="lnlinks" href="#hl-5-7"> 7</a> </span><span class="lnt" id="hl-5-8"><a class="lnlinks" href="#hl-5-8"> 8</a> </span><span class="lnt" id="hl-5-9"><a class="lnlinks" href="#hl-5-9"> 9</a> </span><span class="lnt" id="hl-5-10"><a class="lnlinks" href="#hl-5-10">10</a> </span><span class="lnt" id="hl-5-11"><a class="lnlinks" href="#hl-5-11">11</a> </span><span class="lnt" id="hl-5-12"><a class="lnlinks" href="#hl-5-12">12</a> </span><span class="lnt" id="hl-5-13"><a class="lnlinks" href="#hl-5-13">13</a> </span><span class="lnt" id="hl-5-14"><a class="lnlinks" href="#hl-5-14">14</a> </span><span class="lnt" id="hl-5-15"><a class="lnlinks" href="#hl-5-15">15</a> </span><span class="lnt" id="hl-5-16"><a class="lnlinks" href="#hl-5-16">16</a> </span><span class="lnt" id="hl-5-17"><a class="lnlinks" href="#hl-5-17">17</a> </span><span class="lnt" id="hl-5-18"><a class="lnlinks" href="#hl-5-18">18</a> </span><span class="lnt" id="hl-5-19"><a class="lnlinks" href="#hl-5-19">19</a> </span><span class="lnt" id="hl-5-20"><a class="lnlinks" href="#hl-5-20">20</a> </span><span class="lnt" id="hl-5-21"><a class="lnlinks" href="#hl-5-21">21</a> </span><span class="lnt" id="hl-5-22"><a class="lnlinks" href="#hl-5-22">22</a> </span><span class="lnt" id="hl-5-23"><a class="lnlinks" href="#hl-5-23">23</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Somewhere in our principal application... </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@Path</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">AppResource</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">DemoConfig</span><span class="w"> </span><span class="n">demoConfig</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">DemoService</span><span class="w"> </span><span class="n">demoService</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">AppResource</span><span class="p">(</span><span class="n">DemoConfig</span><span class="w"> </span><span class="n">demoConfig</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">demoConfig</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">demoConfig</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoService</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">QuarkusRestClientBuilder</span><span class="p">.</span><span class="na">newBuilder</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">baseUri</span><span class="p">(</span><span class="n">URI</span><span class="p">.</span><span class="na">create</span><span class="p">(</span><span class="n">demoConfig</span><span class="p">.</span><span class="na">getUrl</span><span class="p">()))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">build</span><span class="p">(</span><span class="n">ExtensionsService</span><span class="p">.</span><span class="na">class</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@GET</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Produces</span><span class="p">(</span><span class="n">MediaType</span><span class="p">.</span><span class="na">APPLICATION_JSON</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Set</span><span class="o">&lt;</span><span class="n">Demo</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">fetchDemo</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">demoService</span><span class="p">.</span><span class="na">fetch</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Dans notre application principale, nous injections notre objet <code>DemoConfig</code> contenant l&rsquo;URL de notre API et nous nous en servons pour configurer un <code>RestClient</code> de manière <a href="https://quarkus.io/guides/rest-client#programmatic-client-creation-with-quarkusrestclientbuilder"target="_blank" rel="external nofollow noopener noreferrer">programmatique</a>. Notre méthode <code>fetchDemo</code> retourne un <code>Set</code> d&rsquo;object <code>Demo</code> qui est un simple POJO. Notre application principale est maintenant reliée à notre API et le tout de manière automatique. Mais cela peut encore être amélioré. Et si notre <code>RestClient</code> n&rsquo;était pas créé par notre application principale, mais par notre extension et injectable comme notre DemoConfig ?</p> <h2 class="heading-element" id="migrer-le-restclient-dans-notre-extension"><span>Migrer le RestClient dans notre extension</span> <a href="#migrer-le-restclient-dans-notre-extension" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Pour faire cela, c&rsquo;est assez simple. Dans un premier temps nous allons déplacer notre classe <code>DemoService</code> dans notre extension, dans le package <code>runtime</code>. Ensuite, nous allons créer une classe <code>DemoResource</code> en charge de créer notre <code>RestClient</code>.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a class="lnlinks" href="#hl-6-1"> 1</a> </span><span class="lnt" id="hl-6-2"><a class="lnlinks" href="#hl-6-2"> 2</a> </span><span class="lnt" id="hl-6-3"><a class="lnlinks" href="#hl-6-3"> 3</a> </span><span class="lnt" id="hl-6-4"><a class="lnlinks" href="#hl-6-4"> 4</a> </span><span class="lnt" id="hl-6-5"><a class="lnlinks" href="#hl-6-5"> 5</a> </span><span class="lnt" id="hl-6-6"><a class="lnlinks" href="#hl-6-6"> 6</a> </span><span class="lnt" id="hl-6-7"><a class="lnlinks" href="#hl-6-7"> 7</a> </span><span class="lnt" id="hl-6-8"><a class="lnlinks" href="#hl-6-8"> 8</a> </span><span class="lnt" id="hl-6-9"><a class="lnlinks" href="#hl-6-9"> 9</a> </span><span class="lnt" id="hl-6-10"><a class="lnlinks" href="#hl-6-10">10</a> </span><span class="lnt" id="hl-6-11"><a class="lnlinks" href="#hl-6-11">11</a> </span><span class="lnt" id="hl-6-12"><a class="lnlinks" href="#hl-6-12">12</a> </span><span class="lnt" id="hl-6-13"><a class="lnlinks" href="#hl-6-13">13</a> </span><span class="lnt" id="hl-6-14"><a class="lnlinks" href="#hl-6-14">14</a> </span><span class="lnt" id="hl-6-15"><a class="lnlinks" href="#hl-6-15">15</a> </span><span class="lnt" id="hl-6-16"><a class="lnlinks" href="#hl-6-16">16</a> </span><span class="lnt" id="hl-6-17"><a class="lnlinks" href="#hl-6-17">17</a> </span><span class="lnt" id="hl-6-18"><a class="lnlinks" href="#hl-6-18">18</a> </span><span class="lnt" id="hl-6-19"><a class="lnlinks" href="#hl-6-19">19</a> </span><span class="lnt" id="hl-6-20"><a class="lnlinks" href="#hl-6-20">20</a> </span><span class="lnt" id="hl-6-21"><a class="lnlinks" href="#hl-6-21">21</a> </span><span class="lnt" id="hl-6-22"><a class="lnlinks" href="#hl-6-22">22</a> </span><span class="lnt" id="hl-6-23"><a class="lnlinks" href="#hl-6-23">23</a> </span><span class="lnt" id="hl-6-24"><a class="lnlinks" href="#hl-6-24">24</a> </span><span class="lnt" id="hl-6-25"><a class="lnlinks" href="#hl-6-25">25</a> </span><span class="lnt" id="hl-6-26"><a class="lnlinks" href="#hl-6-26">26</a> </span><span class="lnt" id="hl-6-27"><a class="lnlinks" href="#hl-6-27">27</a> </span><span class="lnt" id="hl-6-28"><a class="lnlinks" href="#hl-6-28">28</a> </span><span class="lnt" id="hl-6-29"><a class="lnlinks" href="#hl-6-29">29</a> </span><span class="lnt" id="hl-6-30"><a class="lnlinks" href="#hl-6-30">30</a> </span><span class="lnt" id="hl-6-31"><a class="lnlinks" href="#hl-6-31">31</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">info.techland.demo.extension.runtime</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.quarkus.rest.client.reactive.QuarkusRestClientBuilder</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.smallrye.mutiny.Uni</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">jakarta.ws.rs.GET</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">jakarta.ws.rs.Path</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.net.URI</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.List</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@Path</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoResource</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">DemoService</span><span class="w"> </span><span class="n">demoService</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Only for CDI injection</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@SuppressWarnings</span><span class="p">(</span><span class="s">&#34;unused&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="nf">DemoResource</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoResource</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">demoService</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">QuarkusRestClientBuilder</span><span class="p">.</span><span class="na">newBuilder</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">baseUri</span><span class="p">(</span><span class="n">URI</span><span class="p">.</span><span class="na">create</span><span class="p">(</span><span class="n">url</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">build</span><span class="p">(</span><span class="n">DemoService</span><span class="p">.</span><span class="na">class</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@GET</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Set</span><span class="o">&lt;</span><span class="n">Demo</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">fetchAsync</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">demoService</span><span class="p">.</span><span class="na">fetchAsync</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Le POJO <code>Demo</code> est aussi déplacé dans notre extension afin disponible dans n&rsquo;importe quelle application utilisant notre extension.</p> <p>Ensuite dans notre <code>DemoServiceRecorder</code>, nous allons ajouter l&rsquo;instanciation de notre <code>DemoResource</code> :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-7-1"><a class="lnlinks" href="#hl-7-1"> 1</a> </span><span class="lnt" id="hl-7-2"><a class="lnlinks" href="#hl-7-2"> 2</a> </span><span class="lnt" id="hl-7-3"><a class="lnlinks" href="#hl-7-3"> 3</a> </span><span class="lnt" id="hl-7-4"><a class="lnlinks" href="#hl-7-4"> 4</a> </span><span class="lnt" id="hl-7-5"><a class="lnlinks" href="#hl-7-5"> 5</a> </span><span class="lnt" id="hl-7-6"><a class="lnlinks" href="#hl-7-6"> 6</a> </span><span class="lnt" id="hl-7-7"><a class="lnlinks" href="#hl-7-7"> 7</a> </span><span class="lnt" id="hl-7-8"><a class="lnlinks" href="#hl-7-8"> 8</a> </span><span class="lnt" id="hl-7-9"><a class="lnlinks" href="#hl-7-9"> 9</a> </span><span class="lnt" id="hl-7-10"><a class="lnlinks" href="#hl-7-10">10</a> </span><span class="lnt" id="hl-7-11"><a class="lnlinks" href="#hl-7-11">11</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Recorder</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoServiceRecorder</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">RuntimeValue</span><span class="o">&lt;</span><span class="n">DemoResource</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">createSDK</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RuntimeValue</span><span class="o">&lt;&gt;</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">DemoResource</span><span class="p">(</span><span class="n">url</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">RuntimeValue</span><span class="o">&lt;</span><span class="n">DemoConfig</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">createConfig</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RuntimeValue</span><span class="o">&lt;&gt;</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">DemoConfig</span><span class="p">(</span><span class="n">url</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Dernière étape, configurer Quarkus pour injecter notre <code>DemoResource</code> via un <code>SyntheticBean</code> dans notre <code>DemoClientApiProcessor</code> :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-8-1"><a class="lnlinks" href="#hl-8-1"> 1</a> </span><span class="lnt" id="hl-8-2"><a class="lnlinks" href="#hl-8-2"> 2</a> </span><span class="lnt" id="hl-8-3"><a class="lnlinks" href="#hl-8-3"> 3</a> </span><span class="lnt" id="hl-8-4"><a class="lnlinks" href="#hl-8-4"> 4</a> </span><span class="lnt" id="hl-8-5"><a class="lnlinks" href="#hl-8-5"> 5</a> </span><span class="lnt" id="hl-8-6"><a class="lnlinks" href="#hl-8-6"> 6</a> </span><span class="lnt" id="hl-8-7"><a class="lnlinks" href="#hl-8-7"> 7</a> </span><span class="lnt" id="hl-8-8"><a class="lnlinks" href="#hl-8-8"> 8</a> </span><span class="lnt" id="hl-8-9"><a class="lnlinks" href="#hl-8-9"> 9</a> </span><span class="lnt" id="hl-8-10"><a class="lnlinks" href="#hl-8-10">10</a> </span><span class="lnt" id="hl-8-11"><a class="lnlinks" href="#hl-8-11">11</a> </span><span class="lnt" id="hl-8-12"><a class="lnlinks" href="#hl-8-12">12</a> </span><span class="lnt" id="hl-8-13"><a class="lnlinks" href="#hl-8-13">13</a> </span><span class="lnt" id="hl-8-14"><a class="lnlinks" href="#hl-8-14">14</a> </span><span class="lnt" id="hl-8-15"><a class="lnlinks" href="#hl-8-15">15</a> </span><span class="lnt" id="hl-8-16"><a class="lnlinks" href="#hl-8-16">16</a> </span><span class="lnt" id="hl-8-17"><a class="lnlinks" href="#hl-8-17">17</a> </span><span class="lnt" id="hl-8-18"><a class="lnlinks" href="#hl-8-18">18</a> </span><span class="lnt" id="hl-8-19"><a class="lnlinks" href="#hl-8-19">19</a> </span><span class="lnt" id="hl-8-20"><a class="lnlinks" href="#hl-8-20">20</a> </span><span class="lnt" id="hl-8-21"><a class="lnlinks" href="#hl-8-21">21</a> </span><span class="lnt" id="hl-8-22"><a class="lnlinks" href="#hl-8-22">22</a> </span><span class="lnt" id="hl-8-23"><a class="lnlinks" href="#hl-8-23">23</a> </span><span class="lnt" id="hl-8-24"><a class="lnlinks" href="#hl-8-24">24</a> </span><span class="lnt" id="hl-8-25"><a class="lnlinks" href="#hl-8-25">25</a> </span><span class="lnt" id="hl-8-26"><a class="lnlinks" href="#hl-8-26">26</a> </span><span class="lnt" id="hl-8-27"><a class="lnlinks" href="#hl-8-27">27</a> </span><span class="lnt" id="hl-8-28"><a class="lnlinks" href="#hl-8-28">28</a> </span><span class="lnt" id="hl-8-29"><a class="lnlinks" href="#hl-8-29">29</a> </span><span class="lnt" id="hl-8-30"><a class="lnlinks" href="#hl-8-30">30</a> </span><span class="lnt" id="hl-8-31"><a class="lnlinks" href="#hl-8-31">31</a> </span><span class="lnt" id="hl-8-32"><a class="lnlinks" href="#hl-8-32">32</a> </span><span class="lnt" id="hl-8-33"><a class="lnlinks" href="#hl-8-33">33</a> </span><span class="lnt" id="hl-8-34"><a class="lnlinks" href="#hl-8-34">34</a> </span><span class="lnt" id="hl-8-35"><a class="lnlinks" href="#hl-8-35">35</a> </span><span class="lnt" id="hl-8-36"><a class="lnlinks" href="#hl-8-36">36</a> </span><span class="lnt" id="hl-8-37"><a class="lnlinks" href="#hl-8-37">37</a> </span><span class="lnt" id="hl-8-38"><a class="lnlinks" href="#hl-8-38">38</a> </span><span class="lnt" id="hl-8-39"><a class="lnlinks" href="#hl-8-39">39</a> </span><span class="lnt" id="hl-8-40"><a class="lnlinks" href="#hl-8-40">40</a> </span><span class="lnt" id="hl-8-41"><a class="lnlinks" href="#hl-8-41">41</a> </span><span class="lnt" id="hl-8-42"><a class="lnlinks" href="#hl-8-42">42</a> </span><span class="lnt" id="hl-8-43"><a class="lnlinks" href="#hl-8-43">43</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@BuildSteps</span><span class="p">(</span><span class="n">onlyIf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">GlobalDevServicesConfig</span><span class="p">.</span><span class="na">Enabled</span><span class="p">.</span><span class="na">class</span><span class="w"> </span><span class="p">})</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoClientApiProcessor</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Record</span><span class="p">(</span><span class="n">ExecutionTime</span><span class="p">.</span><span class="na">RUNTIME_INIT</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildStep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">createSDK</span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">DevServicesResultBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">devServicesResultBuildItem</span><span class="p">,</span><span class="w"> </span><span class="n">DemoServiceRecorder</span><span class="w"> </span><span class="n">recorder</span><span class="p">,</span><span class="w"> </span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">SyntheticBeanBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">syntheticBeanBuildItemBuildProducer</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Retrieve config set Inside DemoExtensionProcessor</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">apiUrl</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">devServicesResultBuildItem</span><span class="p">.</span><span class="na">stream</span><span class="p">().</span><span class="na">filter</span><span class="p">(</span><span class="n">devService</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">devService</span><span class="p">.</span><span class="na">getName</span><span class="p">().</span><span class="na">equals</span><span class="p">(</span><span class="n">DemoServiceContainer</span><span class="p">.</span><span class="na">CONTAINER_NAME</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">findFirst</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">orElseThrow</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalStateException</span><span class="p">(</span><span class="s">&#34;Can&#39;t find Demo-service url&#34;</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">getConfig</span><span class="p">().</span><span class="na">get</span><span class="p">(</span><span class="s">&#34;url&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">syntheticBeanBuildItemBuildProducer</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="n">SyntheticBeanBuildItem</span><span class="p">.</span><span class="na">configure</span><span class="p">(</span><span class="n">DemoConfig</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">unremovable</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">setRuntimeInit</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">runtimeValue</span><span class="p">(</span><span class="n">recorder</span><span class="p">.</span><span class="na">createConfig</span><span class="p">(</span><span class="s">&#34;http://&#34;</span><span class="o">+</span><span class="n">apiUrl</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">done</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">syntheticBeanBuildItemBuildProducer</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="n">SyntheticBeanBuildItem</span><span class="p">.</span><span class="na">configure</span><span class="p">(</span><span class="n">DemoResource</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">unremovable</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">setRuntimeInit</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">runtimeValue</span><span class="p">(</span><span class="n">recorder</span><span class="p">.</span><span class="na">createSDK</span><span class="p">(</span><span class="s">&#34;http://&#34;</span><span class="o">+</span><span class="n">apiUrl</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">done</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildStep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">addSDKBeans</span><span class="p">(</span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">AdditionalBeanBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">additionalBeans</span><span class="p">,</span><span class="w"> </span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">AdditionalIndexedClassesBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">additionalIndexedClasses</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">additionalBeans</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">AdditionalBeanBuildItem</span><span class="p">(</span><span class="n">DemoService</span><span class="p">.</span><span class="na">class</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">additionalIndexedClasses</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">AdditionalIndexedClassesBuildItem</span><span class="p">(</span><span class="n">DemoService</span><span class="p">.</span><span class="na">class</span><span class="p">.</span><span class="na">getName</span><span class="p">()));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * A jandex file is mandatory by the maven-quarkus-plugin to resolve CDI dependency in the package phase. </span></span></span><span class="line"><span class="cl"><span class="cm"> * At runtime, we need to remove this beans because we provide our own via syntheticBean </span></span></span><span class="line"><span class="cl"><span class="cm"> * @param buildExclusionsBuildItemBuildProducer Producer for beans that will be excluded </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildStep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">removeJandexDemoBean</span><span class="p">(</span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">ExcludedTypeBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">buildExclusionsBuildItemBuildProducer</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">buildExclusionsBuildItemBuildProducer</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">ExcludedTypeBuildItem</span><span class="p">(</span><span class="n">DemoConfig</span><span class="p">.</span><span class="na">class</span><span class="p">.</span><span class="na">getName</span><span class="p">()));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">buildExclusionsBuildItemBuildProducer</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">ExcludedTypeBuildItem</span><span class="p">(</span><span class="n">DemoResource</span><span class="p">.</span><span class="na">class</span><span class="p">.</span><span class="na">getName</span><span class="p">()));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Notre <code>RestClient</code> est maintenant utilisable via injection d&rsquo;un <code>DemoResource</code> dans notre application. Reprenons le code de notre <code>AppResource</code> qui est maintenant beaucoup plus simple :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-9-1"><a class="lnlinks" href="#hl-9-1"> 1</a> </span><span class="lnt" id="hl-9-2"><a class="lnlinks" href="#hl-9-2"> 2</a> </span><span class="lnt" id="hl-9-3"><a class="lnlinks" href="#hl-9-3"> 3</a> </span><span class="lnt" id="hl-9-4"><a class="lnlinks" href="#hl-9-4"> 4</a> </span><span class="lnt" id="hl-9-5"><a class="lnlinks" href="#hl-9-5"> 5</a> </span><span class="lnt" id="hl-9-6"><a class="lnlinks" href="#hl-9-6"> 6</a> </span><span class="lnt" id="hl-9-7"><a class="lnlinks" href="#hl-9-7"> 7</a> </span><span class="lnt" id="hl-9-8"><a class="lnlinks" href="#hl-9-8"> 8</a> </span><span class="lnt" id="hl-9-9"><a class="lnlinks" href="#hl-9-9"> 9</a> </span><span class="lnt" id="hl-9-10"><a class="lnlinks" href="#hl-9-10">10</a> </span><span class="lnt" id="hl-9-11"><a class="lnlinks" href="#hl-9-11">11</a> </span><span class="lnt" id="hl-9-12"><a class="lnlinks" href="#hl-9-12">12</a> </span><span class="lnt" id="hl-9-13"><a class="lnlinks" href="#hl-9-13">13</a> </span><span class="lnt" id="hl-9-14"><a class="lnlinks" href="#hl-9-14">14</a> </span><span class="lnt" id="hl-9-15"><a class="lnlinks" href="#hl-9-15">15</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Path</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">AppResource</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">DemoResource</span><span class="w"> </span><span class="n">demoResource</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">AppResource</span><span class="p">(</span><span class="n">DemoResource</span><span class="w"> </span><span class="n">demoResource</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">demoResource</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">demoResource</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@GET</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Produces</span><span class="p">(</span><span class="n">MediaType</span><span class="p">.</span><span class="na">APPLICATION_JSON</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Set</span><span class="o">&lt;</span><span class="n">Demo</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">fetchDemo</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">demoResource</span><span class="p">.</span><span class="na">fetch</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><h2 class="heading-element" id="conclusion"><span>Conclusion</span> <a href="#conclusion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Voilà qui conclue notre série d&rsquo;articles sur l&rsquo;utilisation des extensions Quarkus pour développeur des devs services personnalisés. Pour les consommateurs de nos API, l&rsquo;utilisation est grandement simplifiée. Un simple import Maven suffit. Un SDK est disponible rendant l&rsquo;appelle à notre API très simple. Et les évolutions de notre service sont maintenant aussi simple que la mise à jour de la dépendance Maven dans le projet. Lors d&rsquo;un changement dans notre API cela pourra possiblement être réalisé de manière transparente par le SDK et sinon sera détectable à la compilation via un changement de signature de méthode Java.</p> Quarkus Extension Dev Service Personnalisé - Partie II https://techland.info/posts/quarkus/quarkus-extension-custom-dev-service-part-2/ Thu, 10 Oct 2024 15:07:03 +0200[email protected] (LE BARO Romain) https://techland.info/posts/quarkus/quarkus-extension-custom-dev-service-part-2/ Développement <p>Dans le <a href="https://techland.info/posts/quarkus/quarkus-extension-custom-dev-service-part-1/">précédent article</a>, nous avons vu comment lancer une base de données automatiquement grâce à une extension Quarkus. Nous allons maintenant voir comment démarrer notre service de la même manière et lui faire utiliser la base de données que nous venons de déployer. Je ne reviendrai pas sur les points abordés dans le précédent article. Je vous invite donc à le lire avant de continuer.</p> <h2 class="heading-element" id="on-prend-les-mêmes-et-on-recommence"><span>On prend les mêmes et on recommence.</span> <a href="#on-prend-les-m%c3%aames-et-on-recommence" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>La création de notre conteneur pour notre service se passe de la même manière que la base de données. On va donc commencer par ajouter les configurations nécessaires à notre <code>DevServicesConfig</code> :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a> </span><span class="lnt" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a> </span><span class="lnt" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a> </span><span class="lnt" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a> </span><span class="lnt" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a> </span><span class="lnt" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a> </span><span class="lnt" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a> </span><span class="lnt" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a> </span><span class="lnt" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a> </span><span class="lnt" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a> </span><span class="lnt" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a> </span><span class="lnt" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a> </span><span class="lnt" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a> </span><span class="lnt" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a> </span><span class="lnt" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a> </span><span class="lnt" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a> </span><span class="lnt" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a> </span><span class="lnt" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a> </span><span class="lnt" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a> </span><span class="lnt" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a> </span><span class="lnt" id="hl-0-22"><a class="lnlinks" href="#hl-0-22">22</a> </span><span class="lnt" id="hl-0-23"><a class="lnlinks" href="#hl-0-23">23</a> </span><span class="lnt" id="hl-0-24"><a class="lnlinks" href="#hl-0-24">24</a> </span><span class="lnt" id="hl-0-25"><a class="lnlinks" href="#hl-0-25">25</a> </span><span class="lnt" id="hl-0-26"><a class="lnlinks" href="#hl-0-26">26</a> </span><span class="lnt" id="hl-0-27"><a class="lnlinks" href="#hl-0-27">27</a> </span><span class="lnt" id="hl-0-28"><a class="lnlinks" href="#hl-0-28">28</a> </span><span class="lnt" id="hl-0-29"><a class="lnlinks" href="#hl-0-29">29</a> </span><span class="lnt" id="hl-0-30"><a class="lnlinks" href="#hl-0-30">30</a> </span><span class="lnt" id="hl-0-31"><a class="lnlinks" href="#hl-0-31">31</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The container image name to use, for container based DevServices providers. </span></span></span><span class="line"><span class="cl"><span class="cm"> * If you want to use a specific version of demo, use: </span></span></span><span class="line"><span class="cl"><span class="cm"> * {@code demo:&lt;version&gt;}. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;scandinave/demo&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">imageName</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Optional fixed port the dev service will listen to. </span></span></span><span class="line"><span class="cl"><span class="cm"> * &lt;p&gt; </span></span></span><span class="line"><span class="cl"><span class="cm"> * If not defined, the port will be chosen randomly. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">OptionalInt</span><span class="w"> </span><span class="nf">port</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The value of the {@code quarkus-dev-service-demo} label attached to the started container. </span></span></span><span class="line"><span class="cl"><span class="cm"> * This property is used when {@code shared} is set to {@code true}. </span></span></span><span class="line"><span class="cl"><span class="cm"> * In this case, before starting a container, Dev Services for Demo looks for a container with the </span></span></span><span class="line"><span class="cl"><span class="cm"> * {@code quarkus-dev-service-demo} label set to the configured value. </span></span></span><span class="line"><span class="cl"><span class="cm"> * If found, it will use this container instead of starting a new one. Otherwise, it </span></span></span><span class="line"><span class="cl"><span class="cm"> * starts a new container with the {@code quarkus-dev-service-demo} label set to the specified value. </span></span></span><span class="line"><span class="cl"><span class="cm"> * &lt;p&gt; </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;demo&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">serviceName</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Environment variables that are passed to the container. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">containerEnv</span><span class="p">();</span></span></span></code></pre></td></tr></table> </div> </div><p>On commence par ajouter une option pour le nom de l&rsquo;image docker qui servira à démarrer notre conteneur. De cette manière, l&rsquo;utilisateur pourra choisir une version spécifique s&rsquo;il le souhaite. Le port est par défaut aléatoire, mais pourra également être précisé. Enfin, le label appliqué au conteneur pourra aussi être personnalisé.</p> <h3 class="heading-element" id="demoextensionprocessor"><span>DemoExtensionProcessor</span> <a href="#demoextensionprocessor" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>Dans notre <code>DemoExtensionProcessor</code>, nous ajoutons le code permettant d&rsquo;initialiser le conteneur de notre service.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a> </span><span class="lnt" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a> </span><span class="lnt" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a> </span><span class="lnt" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a> </span><span class="lnt" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a> </span><span class="lnt" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a> </span><span class="lnt" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a> </span><span class="lnt" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a> </span><span class="lnt" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a> </span><span class="lnt" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a> </span><span class="lnt" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a> </span><span class="lnt" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a> </span><span class="lnt" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a> </span><span class="lnt" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a> </span><span class="lnt" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a> </span><span class="lnt" id="hl-1-16"><a class="lnlinks" href="#hl-1-16">16</a> </span><span class="lnt" id="hl-1-17"><a class="lnlinks" href="#hl-1-17">17</a> </span><span class="lnt" id="hl-1-18"><a class="lnlinks" href="#hl-1-18">18</a> </span><span class="lnt" id="hl-1-19"><a class="lnlinks" href="#hl-1-19">19</a> </span><span class="lnt" id="hl-1-20"><a class="lnlinks" href="#hl-1-20">20</a> </span><span class="lnt" id="hl-1-21"><a class="lnlinks" href="#hl-1-21">21</a> </span><span class="lnt" id="hl-1-22"><a class="lnlinks" href="#hl-1-22">22</a> </span><span class="lnt" id="hl-1-23"><a class="lnlinks" href="#hl-1-23">23</a> </span><span class="lnt" id="hl-1-24"><a class="lnlinks" href="#hl-1-24">24</a> </span><span class="lnt" id="hl-1-25"><a class="lnlinks" href="#hl-1-25">25</a> </span><span class="lnt" id="hl-1-26"><a class="lnlinks" href="#hl-1-26">26</a> </span><span class="lnt" id="hl-1-27"><a class="lnlinks" href="#hl-1-27">27</a> </span><span class="lnt" id="hl-1-28"><a class="lnlinks" href="#hl-1-28">28</a> </span><span class="lnt" id="hl-1-29"><a class="lnlinks" href="#hl-1-29">29</a> </span><span class="lnt" id="hl-1-30"><a class="lnlinks" href="#hl-1-30">30</a> </span><span class="lnt" id="hl-1-31"><a class="lnlinks" href="#hl-1-31">31</a> </span><span class="lnt" id="hl-1-32"><a class="lnlinks" href="#hl-1-32">32</a> </span><span class="lnt" id="hl-1-33"><a class="lnlinks" href="#hl-1-33">33</a> </span><span class="lnt" id="hl-1-34"><a class="lnlinks" href="#hl-1-34">34</a> </span><span class="lnt" id="hl-1-35"><a class="lnlinks" href="#hl-1-35">35</a> </span><span class="lnt" id="hl-1-36"><a class="lnlinks" href="#hl-1-36">36</a> </span><span class="lnt" id="hl-1-37"><a class="lnlinks" href="#hl-1-37">37</a> </span><span class="lnt" id="hl-1-38"><a class="lnlinks" href="#hl-1-38">38</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildSteps</span><span class="p">(</span><span class="n">onlyIf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="n">GlobalDevServicesConfig</span><span class="p">.</span><span class="na">Enabled</span><span class="p">.</span><span class="na">class</span><span class="p">})</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoExtensionProcessor</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** stuff from previous article </span></span></span><span class="line"><span class="cl"><span class="cm"> * ... </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">volatile</span><span class="w"> </span><span class="n">RunningDevService</span><span class="w"> </span><span class="n">demoApiDevService</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildStep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">createContainer</span><span class="p">(</span><span class="n">DockerStatusBuildItem</span><span class="w"> </span><span class="n">dockerStatusBuildItem</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">DevServicesSharedNetworkBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">devServicesSharedNetworkBuildItem</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">DemoBuildTimeConfig</span><span class="w"> </span><span class="n">buildTimeConfig</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">GlobalDevServicesConfig</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">DevServicesResultBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">devServicesResult</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">runningConfiguration</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">buildTimeConfig</span><span class="p">.</span><span class="na">devservices</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// DemoApiRunningDevService newAPIDevService = startDemoContainer...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">DemoApiRunningDevService</span><span class="w"> </span><span class="n">newAPIDevService</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">startDemoContainer</span><span class="p">(</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">dockerStatusBuildItem</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">!</span><span class="n">devServicesSharedNetworkBuildItem</span><span class="p">.</span><span class="na">isEmpty</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">timeout</span><span class="p">.</span><span class="na">orElse</span><span class="p">(</span><span class="n">Duration</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">0</span><span class="p">,</span><span class="w"> </span><span class="n">ChronoUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">)),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">runningConfiguration</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">newDatabaseDevService</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">errors</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Get the RunningDevService instance.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoApiDevService</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">newAPIDevService</span><span class="p">.</span><span class="na">getRunningDevService</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">Throwable</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RuntimeException</span><span class="p">(</span><span class="n">t</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Log</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">&#34;Dev Services for Demo started.&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">devServicesResult</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="n">demoApiDevService</span><span class="p">.</span><span class="na">toBuildItem</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Notre méthode <code>startDemoContainer</code> prends en plus en paramètre le résultat de l&rsquo;appel à la méthode <code>startDatabaseContainer</code> qui contient une instance d&rsquo;un <code>DevRunningDevService</code>. Dans ce dernier, nous avions enregistré l&rsquo;URL d&rsquo;accès à notre base de données. Je vous avais dit que cela servirait plus tard.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a> </span><span class="lnt" id="hl-2-2"><a class="lnlinks" href="#hl-2-2">2</a> </span><span class="lnt" id="hl-2-3"><a class="lnlinks" href="#hl-2-3">3</a> </span><span class="lnt" id="hl-2-4"><a class="lnlinks" href="#hl-2-4">4</a> </span><span class="lnt" id="hl-2-5"><a class="lnlinks" href="#hl-2-5">5</a> </span><span class="lnt" id="hl-2-6"><a class="lnlinks" href="#hl-2-6">6</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="p">(</span><span class="n">feature</span><span class="p">,</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">getContainerId</span><span class="p">(),</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">::</span><span class="n">close</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">&#34;url&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">getHost</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;:&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">getPort</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">&#34;url-internal&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">getContainerName</span><span class="p">().</span><span class="na">substring</span><span class="p">(</span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;:&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">POSTGRES_INTERNAL_PORT</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div><h3 class="heading-element" id="startdemocontainer"><span>startDemoContainer</span> <a href="#startdemocontainer" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a class="lnlinks" href="#hl-3-1"> 1</a> </span><span class="lnt" id="hl-3-2"><a class="lnlinks" href="#hl-3-2"> 2</a> </span><span class="lnt" id="hl-3-3"><a class="lnlinks" href="#hl-3-3"> 3</a> </span><span class="lnt" id="hl-3-4"><a class="lnlinks" href="#hl-3-4"> 4</a> </span><span class="lnt" id="hl-3-5"><a class="lnlinks" href="#hl-3-5"> 5</a> </span><span class="lnt" id="hl-3-6"><a class="lnlinks" href="#hl-3-6"> 6</a> </span><span class="lnt" id="hl-3-7"><a class="lnlinks" href="#hl-3-7"> 7</a> </span><span class="lnt" id="hl-3-8"><a class="lnlinks" href="#hl-3-8"> 8</a> </span><span class="lnt" id="hl-3-9"><a class="lnlinks" href="#hl-3-9"> 9</a> </span><span class="lnt" id="hl-3-10"><a class="lnlinks" href="#hl-3-10">10</a> </span><span class="lnt" id="hl-3-11"><a class="lnlinks" href="#hl-3-11">11</a> </span><span class="lnt" id="hl-3-12"><a class="lnlinks" href="#hl-3-12">12</a> </span><span class="lnt" id="hl-3-13"><a class="lnlinks" href="#hl-3-13">13</a> </span><span class="lnt" id="hl-3-14"><a class="lnlinks" href="#hl-3-14">14</a> </span><span class="lnt" id="hl-3-15"><a class="lnlinks" href="#hl-3-15">15</a> </span><span class="lnt" id="hl-3-16"><a class="lnlinks" href="#hl-3-16">16</a> </span><span class="lnt" id="hl-3-17"><a class="lnlinks" href="#hl-3-17">17</a> </span><span class="lnt" id="hl-3-18"><a class="lnlinks" href="#hl-3-18">18</a> </span><span class="lnt" id="hl-3-19"><a class="lnlinks" href="#hl-3-19">19</a> </span><span class="lnt" id="hl-3-20"><a class="lnlinks" href="#hl-3-20">20</a> </span><span class="lnt" id="hl-3-21"><a class="lnlinks" href="#hl-3-21">21</a> </span><span class="lnt" id="hl-3-22"><a class="lnlinks" href="#hl-3-22">22</a> </span><span class="lnt" id="hl-3-23"><a class="lnlinks" href="#hl-3-23">23</a> </span><span class="lnt" id="hl-3-24"><a class="lnlinks" href="#hl-3-24">24</a> </span><span class="lnt" id="hl-3-25"><a class="lnlinks" href="#hl-3-25">25</a> </span><span class="lnt" id="hl-3-26"><a class="lnlinks" href="#hl-3-26">26</a> </span><span class="lnt" id="hl-3-27"><a class="lnlinks" href="#hl-3-27">27</a> </span><span class="lnt" id="hl-3-28"><a class="lnlinks" href="#hl-3-28">28</a> </span><span class="lnt" id="hl-3-29"><a class="lnlinks" href="#hl-3-29">29</a> </span><span class="lnt" id="hl-3-30"><a class="lnlinks" href="#hl-3-30">30</a> </span><span class="lnt" id="hl-3-31"><a class="lnlinks" href="#hl-3-31">31</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">private</span><span class="w"> </span><span class="n">DemoApiRunningDevService</span><span class="w"> </span><span class="nf">startDemoContainer</span><span class="p">(</span><span class="n">DockerStatusBuildItem</span><span class="w"> </span><span class="n">dockerStatusBuildItem</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">Duration</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">DevServicesConfig</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span><span class="n">DemoDatabaseRunningDevService</span><span class="w"> </span><span class="n">runningService</span><span class="p">,</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">errors</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">enabled</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// explicitly disabled</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Log</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&#34;Not starting Dev Services for Demo as it has been disabled in the config&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">dockerStatusBuildItem</span><span class="p">.</span><span class="na">isDockerAvailable</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Log</span><span class="p">.</span><span class="na">warn</span><span class="p">(</span><span class="s">&#34;Please get a working docker instance&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">errors</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="s">&#34;Please get a working docker instance&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">dbUrl</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">runningService</span><span class="p">.</span><span class="na">getRunningDevService</span><span class="p">().</span><span class="na">getConfig</span><span class="p">()).</span><span class="na">orElseThrow</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalStateException</span><span class="p">(</span><span class="s">&#34;Database does not start properly&#34;</span><span class="p">)).</span><span class="na">get</span><span class="p">(</span><span class="s">&#34;url-internal&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">ContainerAddress</span><span class="o">&gt;</span><span class="w"> </span><span class="n">maybeExistingContainer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">demoAPIDevModeContainerLocator</span><span class="p">.</span><span class="na">locateContainer</span><span class="p">(</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">serviceName</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">shared</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">LaunchMode</span><span class="p">.</span><span class="na">current</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">imageName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">imageName</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">DockerImageName</span><span class="w"> </span><span class="n">dockerImageName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">DockerImageName</span><span class="p">.</span><span class="na">parse</span><span class="p">(</span><span class="n">imageName</span><span class="p">).</span><span class="na">asCompatibleSubstituteFor</span><span class="p">(</span><span class="n">imageName</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">maybeExistingContainer</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">map</span><span class="p">(</span><span class="n">existingContainer</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DemoApiRunningDevService</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">RunningDevService</span><span class="p">(</span><span class="n">DemoApiContainer</span><span class="p">.</span><span class="na">CONTAINER_NAME</span><span class="p">,</span><span class="w"> </span><span class="n">existingContainer</span><span class="p">.</span><span class="na">getId</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="s">&#34;url&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">existingContainer</span><span class="p">.</span><span class="na">getHost</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;:&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">existingContainer</span><span class="p">.</span><span class="na">getPort</span><span class="p">()))))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">orElseGet</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DemoApiRunningDevService</span><span class="p">(</span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span><span class="n">dbUrl</span><span class="p">,</span><span class="w"> </span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">runningService</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>On commence par les mêmes vérifications concernant la présence de docker et l&rsquo;activation des dev services puis nous récupérons l&rsquo;URL <code>interne docker</code> de connexion à la base de données depuis notre instance de <code>RunningDevService</code>. La méthode vérifie ensuite si autre conteneur existe déjà grâce à un nouveau <code>ContainerLocator</code>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a class="lnlinks" href="#hl-4-1">1</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">ContainerLocator</span><span class="w"> </span><span class="n">demoAPIDevModeContainerLocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ContainerLocator</span><span class="p">(</span><span class="n">DemoApiContainer</span><span class="p">.</span><span class="na">DEV_SERVICE_LABEL</span><span class="p">,</span><span class="w"> </span><span class="n">DEMO_INTERNAL_PORT</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div><p>Si un conteneur de notre service est déjà lancé, alors la méthode retourne un <code>RunningDevService</code> avec les informations du conteneur existant. Sinon un nouveau <code>RunningDevService</code> pour le nouveau conteneur est retourné. Ce nouveau <code>RunningDevService</code> prend en paramètre l&rsquo;URL de notre base de données.</p> <h3 class="heading-element" id="demoapirunningdevservice"><span>DemoApiRunningDevService</span> <a href="#demoapirunningdevservice" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a class="lnlinks" href="#hl-5-1"> 1</a> </span><span class="lnt" id="hl-5-2"><a class="lnlinks" href="#hl-5-2"> 2</a> </span><span class="lnt" id="hl-5-3"><a class="lnlinks" href="#hl-5-3"> 3</a> </span><span class="lnt" id="hl-5-4"><a class="lnlinks" href="#hl-5-4"> 4</a> </span><span class="lnt" id="hl-5-5"><a class="lnlinks" href="#hl-5-5"> 5</a> </span><span class="lnt" id="hl-5-6"><a class="lnlinks" href="#hl-5-6"> 6</a> </span><span class="lnt" id="hl-5-7"><a class="lnlinks" href="#hl-5-7"> 7</a> </span><span class="lnt" id="hl-5-8"><a class="lnlinks" href="#hl-5-8"> 8</a> </span><span class="lnt" id="hl-5-9"><a class="lnlinks" href="#hl-5-9"> 9</a> </span><span class="lnt" id="hl-5-10"><a class="lnlinks" href="#hl-5-10">10</a> </span><span class="lnt" id="hl-5-11"><a class="lnlinks" href="#hl-5-11">11</a> </span><span class="lnt" id="hl-5-12"><a class="lnlinks" href="#hl-5-12">12</a> </span><span class="lnt" id="hl-5-13"><a class="lnlinks" href="#hl-5-13">13</a> </span><span class="lnt" id="hl-5-14"><a class="lnlinks" href="#hl-5-14">14</a> </span><span class="lnt" id="hl-5-15"><a class="lnlinks" href="#hl-5-15">15</a> </span><span class="lnt" id="hl-5-16"><a class="lnlinks" href="#hl-5-16">16</a> </span><span class="lnt" id="hl-5-17"><a class="lnlinks" href="#hl-5-17">17</a> </span><span class="lnt" id="hl-5-18"><a class="lnlinks" href="#hl-5-18">18</a> </span><span class="lnt" id="hl-5-19"><a class="lnlinks" href="#hl-5-19">19</a> </span><span class="lnt" id="hl-5-20"><a class="lnlinks" href="#hl-5-20">20</a> </span><span class="lnt" id="hl-5-21"><a class="lnlinks" href="#hl-5-21">21</a> </span><span class="lnt" id="hl-5-22"><a class="lnlinks" href="#hl-5-22">22</a> </span><span class="lnt" id="hl-5-23"><a class="lnlinks" href="#hl-5-23">23</a> </span><span class="lnt" id="hl-5-24"><a class="lnlinks" href="#hl-5-24">24</a> </span><span class="lnt" id="hl-5-25"><a class="lnlinks" href="#hl-5-25">25</a> </span><span class="lnt" id="hl-5-26"><a class="lnlinks" href="#hl-5-26">26</a> </span><span class="lnt" id="hl-5-27"><a class="lnlinks" href="#hl-5-27">27</a> </span><span class="lnt" id="hl-5-28"><a class="lnlinks" href="#hl-5-28">28</a> </span><span class="lnt" id="hl-5-29"><a class="lnlinks" href="#hl-5-29">29</a> </span><span class="lnt" id="hl-5-30"><a class="lnlinks" href="#hl-5-30">30</a> </span><span class="lnt" id="hl-5-31"><a class="lnlinks" href="#hl-5-31">31</a> </span><span class="lnt" id="hl-5-32"><a class="lnlinks" href="#hl-5-32">32</a> </span><span class="lnt" id="hl-5-33"><a class="lnlinks" href="#hl-5-33">33</a> </span><span class="lnt" id="hl-5-34"><a class="lnlinks" href="#hl-5-34">34</a> </span><span class="lnt" id="hl-5-35"><a class="lnlinks" href="#hl-5-35">35</a> </span><span class="lnt" id="hl-5-36"><a class="lnlinks" href="#hl-5-36">36</a> </span><span class="lnt" id="hl-5-37"><a class="lnlinks" href="#hl-5-37">37</a> </span><span class="lnt" id="hl-5-38"><a class="lnlinks" href="#hl-5-38">38</a> </span><span class="lnt" id="hl-5-39"><a class="lnlinks" href="#hl-5-39">39</a> </span><span class="lnt" id="hl-5-40"><a class="lnlinks" href="#hl-5-40">40</a> </span><span class="lnt" id="hl-5-41"><a class="lnlinks" href="#hl-5-41">41</a> </span><span class="lnt" id="hl-5-42"><a class="lnlinks" href="#hl-5-42">42</a> </span><span class="lnt" id="hl-5-43"><a class="lnlinks" href="#hl-5-43">43</a> </span><span class="lnt" id="hl-5-44"><a class="lnlinks" href="#hl-5-44">44</a> </span><span class="lnt" id="hl-5-45"><a class="lnlinks" href="#hl-5-45">45</a> </span><span class="lnt" id="hl-5-46"><a class="lnlinks" href="#hl-5-46">46</a> </span><span class="lnt" id="hl-5-47"><a class="lnlinks" href="#hl-5-47">47</a> </span><span class="lnt" id="hl-5-48"><a class="lnlinks" href="#hl-5-48">48</a> </span><span class="lnt" id="hl-5-49"><a class="lnlinks" href="#hl-5-49">49</a> </span><span class="lnt" id="hl-5-50"><a class="lnlinks" href="#hl-5-50">50</a> </span><span class="lnt" id="hl-5-51"><a class="lnlinks" href="#hl-5-51">51</a> </span><span class="lnt" id="hl-5-52"><a class="lnlinks" href="#hl-5-52">52</a> </span><span class="lnt" id="hl-5-53"><a class="lnlinks" href="#hl-5-53">53</a> </span><span class="lnt" id="hl-5-54"><a class="lnlinks" href="#hl-5-54">54</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoApiRunningDevService</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">DemoApiContainer</span><span class="w"> </span><span class="n">demoContainer</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="w"> </span><span class="n">runningDevServiceSupplier</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoApiRunningDevService</span><span class="p">(</span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">Duration</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">DevServicesConfig</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">dbUrl</span><span class="p">,</span><span class="w"> </span><span class="n">DockerImageName</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">DemoDatabaseRunningDevService</span><span class="w"> </span><span class="n">runningService</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">runningDevServiceSupplier</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">run</span><span class="p">(</span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span><span class="n">dbUrl</span><span class="p">,</span><span class="w"> </span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">runningService</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoApiRunningDevService</span><span class="p">(</span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="w"> </span><span class="n">runningDevService</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">runningDevServiceSupplier</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">runningDevService</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="w"> </span><span class="nf">run</span><span class="p">(</span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">Duration</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">DevServicesConfig</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">dbUrl</span><span class="p">,</span><span class="w"> </span><span class="n">DockerImageName</span><span class="w"> </span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">DemoDatabaseRunningDevService</span><span class="w"> </span><span class="n">runningService</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">port</span><span class="p">().</span><span class="na">isPresent</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">(</span><span class="n">DemoApiContainer</span><span class="w"> </span><span class="n">container</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DemoApiContainer</span><span class="p">(</span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">port</span><span class="p">().</span><span class="na">getAsInt</span><span class="p">(),</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">shared</span><span class="p">(),</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">serviceName</span><span class="p">()))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">container</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">(</span><span class="n">DemoApiContainer</span><span class="w"> </span><span class="n">container</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DemoApiContainer</span><span class="p">(</span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">shared</span><span class="p">(),</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">serviceName</span><span class="p">()))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">container</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">timeout</span><span class="p">).</span><span class="na">ifPresent</span><span class="p">(</span><span class="n">demoContainer</span><span class="p">::</span><span class="n">withStartupTimeout</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">withEnv</span><span class="p">(</span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">containerEnv</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">withEnv</span><span class="p">(</span><span class="s">&#34;POSTGRES_USER&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">username</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">withEnv</span><span class="p">(</span><span class="s">&#34;POSTGRES_PASSWORD&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">password</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">withEnv</span><span class="p">(</span><span class="s">&#34;DB_URL&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">dbUrl</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;/&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">name</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">runningService</span><span class="p">.</span><span class="na">getDatabaseContainer</span><span class="p">().</span><span class="na">isPresent</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// Wait for database container to start. If container is null, it means that a database was already started so no need to wait.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">withNetwork</span><span class="p">(</span><span class="n">runningService</span><span class="p">.</span><span class="na">getDatabaseContainer</span><span class="p">().</span><span class="na">get</span><span class="p">().</span><span class="na">getNetwork</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">dependsOn</span><span class="p">(</span><span class="n">runningService</span><span class="p">.</span><span class="na">getDatabaseContainer</span><span class="p">().</span><span class="na">get</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">start</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="p">(</span><span class="n">DemoApiContainer</span><span class="p">.</span><span class="na">CONTAINER_NAME</span><span class="p">,</span><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">getContainerId</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoContainer</span><span class="p">::</span><span class="n">close</span><span class="p">,</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="s">&#34;url&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">getHost</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;:&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">demoContainer</span><span class="p">.</span><span class="na">getPort</span><span class="p">()));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">GenericContainer</span><span class="o">&lt;</span><span class="n">DemoApiContainer</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="nf">getDatabaseContainer</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">demoContainer</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="w"> </span><span class="nf">getRunningDevService</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">runningDevServiceSupplier</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Notre classe définit deux constructeurs. Un renvoyant juste le <code>RunningDevService</code> existant dans le cas où le conteneur existe déjà. Le deuxième, créant un nouveau <code>RunningDevService</code>. Notre méthode <code>run</code> commence par vérifier si un port est précisé dans la configuration du dev-service. Si c&rsquo;est le cas, notre conteneur sera démarré sur ce port, sinon, un port aléatoire disponible sera alloué (comportement par défaut).</p> <p>Une fois l&rsquo;instance de notre <code>DemoApiConteneur</code> créée, nous le configurons en lui passant les variables d&rsquo;environnement qu&rsquo;il attend grâce à la méthode <code>withEnv</code>. Nous utilisons pour cela notre <code>devServiceConfig</code> pour récupérer les identifiants de connexion à la base de données puis l&rsquo;URL passée en paramètre. Ensuite, nous configurons le conteneur sur le même réseau que la base de données et pour attendre que celle-ci ait fini de démarrer.</p> <p>Enfin, nous retournons notre <code>RunninDevService</code> contenant l&rsquo;ID de notre conteneur ainsi que de la configuration contenant l&rsquo;URL d&rsquo;accès à notre API qui nous sera également utile pour la suite.</p> <h3 class="heading-element" id="demoapicontainer"><span>DemoApiContainer</span> <a href="#demoapicontainer" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>Il ne nous reste plus qu&rsquo;à voir la classe <code>DemoApiContainer</code> pour finir ce second post sur la création d&rsquo;un DevService avec Quarkus.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a class="lnlinks" href="#hl-6-1"> 1</a> </span><span class="lnt" id="hl-6-2"><a class="lnlinks" href="#hl-6-2"> 2</a> </span><span class="lnt" id="hl-6-3"><a class="lnlinks" href="#hl-6-3"> 3</a> </span><span class="lnt" id="hl-6-4"><a class="lnlinks" href="#hl-6-4"> 4</a> </span><span class="lnt" id="hl-6-5"><a class="lnlinks" href="#hl-6-5"> 5</a> </span><span class="lnt" id="hl-6-6"><a class="lnlinks" href="#hl-6-6"> 6</a> </span><span class="lnt" id="hl-6-7"><a class="lnlinks" href="#hl-6-7"> 7</a> </span><span class="lnt" id="hl-6-8"><a class="lnlinks" href="#hl-6-8"> 8</a> </span><span class="lnt" id="hl-6-9"><a class="lnlinks" href="#hl-6-9"> 9</a> </span><span class="lnt" id="hl-6-10"><a class="lnlinks" href="#hl-6-10">10</a> </span><span class="lnt" id="hl-6-11"><a class="lnlinks" href="#hl-6-11">11</a> </span><span class="lnt" id="hl-6-12"><a class="lnlinks" href="#hl-6-12">12</a> </span><span class="lnt" id="hl-6-13"><a class="lnlinks" href="#hl-6-13">13</a> </span><span class="lnt" id="hl-6-14"><a class="lnlinks" href="#hl-6-14">14</a> </span><span class="lnt" id="hl-6-15"><a class="lnlinks" href="#hl-6-15">15</a> </span><span class="lnt" id="hl-6-16"><a class="lnlinks" href="#hl-6-16">16</a> </span><span class="lnt" id="hl-6-17"><a class="lnlinks" href="#hl-6-17">17</a> </span><span class="lnt" id="hl-6-18"><a class="lnlinks" href="#hl-6-18">18</a> </span><span class="lnt" id="hl-6-19"><a class="lnlinks" href="#hl-6-19">19</a> </span><span class="lnt" id="hl-6-20"><a class="lnlinks" href="#hl-6-20">20</a> </span><span class="lnt" id="hl-6-21"><a class="lnlinks" href="#hl-6-21">21</a> </span><span class="lnt" id="hl-6-22"><a class="lnlinks" href="#hl-6-22">22</a> </span><span class="lnt" id="hl-6-23"><a class="lnlinks" href="#hl-6-23">23</a> </span><span class="lnt" id="hl-6-24"><a class="lnlinks" href="#hl-6-24">24</a> </span><span class="lnt" id="hl-6-25"><a class="lnlinks" href="#hl-6-25">25</a> </span><span class="lnt" id="hl-6-26"><a class="lnlinks" href="#hl-6-26">26</a> </span><span class="lnt" id="hl-6-27"><a class="lnlinks" href="#hl-6-27">27</a> </span><span class="lnt" id="hl-6-28"><a class="lnlinks" href="#hl-6-28">28</a> </span><span class="lnt" id="hl-6-29"><a class="lnlinks" href="#hl-6-29">29</a> </span><span class="lnt" id="hl-6-30"><a class="lnlinks" href="#hl-6-30">30</a> </span><span class="lnt" id="hl-6-31"><a class="lnlinks" href="#hl-6-31">31</a> </span><span class="lnt" id="hl-6-32"><a class="lnlinks" href="#hl-6-32">32</a> </span><span class="lnt" id="hl-6-33"><a class="lnlinks" href="#hl-6-33">33</a> </span><span class="lnt" id="hl-6-34"><a class="lnlinks" href="#hl-6-34">34</a> </span><span class="lnt" id="hl-6-35"><a class="lnlinks" href="#hl-6-35">35</a> </span><span class="lnt" id="hl-6-36"><a class="lnlinks" href="#hl-6-36">36</a> </span><span class="lnt" id="hl-6-37"><a class="lnlinks" href="#hl-6-37">37</a> </span><span class="lnt" id="hl-6-38"><a class="lnlinks" href="#hl-6-38">38</a> </span><span class="lnt" id="hl-6-39"><a class="lnlinks" href="#hl-6-39">39</a> </span><span class="lnt" id="hl-6-40"><a class="lnlinks" href="#hl-6-40">40</a> </span><span class="lnt" id="hl-6-41"><a class="lnlinks" href="#hl-6-41">41</a> </span><span class="lnt" id="hl-6-42"><a class="lnlinks" href="#hl-6-42">42</a> </span><span class="lnt" id="hl-6-43"><a class="lnlinks" href="#hl-6-43">43</a> </span><span class="lnt" id="hl-6-44"><a class="lnlinks" href="#hl-6-44">44</a> </span><span class="lnt" id="hl-6-45"><a class="lnlinks" href="#hl-6-45">45</a> </span><span class="lnt" id="hl-6-46"><a class="lnlinks" href="#hl-6-46">46</a> </span><span class="lnt" id="hl-6-47"><a class="lnlinks" href="#hl-6-47">47</a> </span><span class="lnt" id="hl-6-48"><a class="lnlinks" href="#hl-6-48">48</a> </span><span class="lnt" id="hl-6-49"><a class="lnlinks" href="#hl-6-49">49</a> </span><span class="lnt" id="hl-6-50"><a class="lnlinks" href="#hl-6-50">50</a> </span><span class="lnt" id="hl-6-51"><a class="lnlinks" href="#hl-6-51">51</a> </span><span class="lnt" id="hl-6-52"><a class="lnlinks" href="#hl-6-52">52</a> </span><span class="lnt" id="hl-6-53"><a class="lnlinks" href="#hl-6-53">53</a> </span><span class="lnt" id="hl-6-54"><a class="lnlinks" href="#hl-6-54">54</a> </span><span class="lnt" id="hl-6-55"><a class="lnlinks" href="#hl-6-55">55</a> </span><span class="lnt" id="hl-6-56"><a class="lnlinks" href="#hl-6-56">56</a> </span><span class="lnt" id="hl-6-57"><a class="lnlinks" href="#hl-6-57">57</a> </span><span class="lnt" id="hl-6-58"><a class="lnlinks" href="#hl-6-58">58</a> </span><span class="lnt" id="hl-6-59"><a class="lnlinks" href="#hl-6-59">59</a> </span><span class="lnt" id="hl-6-60"><a class="lnlinks" href="#hl-6-60">60</a> </span><span class="lnt" id="hl-6-61"><a class="lnlinks" href="#hl-6-61">61</a> </span><span class="lnt" id="hl-6-62"><a class="lnlinks" href="#hl-6-62">62</a> </span><span class="lnt" id="hl-6-63"><a class="lnlinks" href="#hl-6-63">63</a> </span><span class="lnt" id="hl-6-64"><a class="lnlinks" href="#hl-6-64">64</a> </span><span class="lnt" id="hl-6-65"><a class="lnlinks" href="#hl-6-65">65</a> </span><span class="lnt" id="hl-6-66"><a class="lnlinks" href="#hl-6-66">66</a> </span><span class="lnt" id="hl-6-67"><a class="lnlinks" href="#hl-6-67">67</a> </span><span class="lnt" id="hl-6-68"><a class="lnlinks" href="#hl-6-68">68</a> </span><span class="lnt" id="hl-6-69"><a class="lnlinks" href="#hl-6-69">69</a> </span><span class="lnt" id="hl-6-70"><a class="lnlinks" href="#hl-6-70">70</a> </span><span class="lnt" id="hl-6-71"><a class="lnlinks" href="#hl-6-71">71</a> </span><span class="lnt" id="hl-6-72"><a class="lnlinks" href="#hl-6-72">72</a> </span><span class="lnt" id="hl-6-73"><a class="lnlinks" href="#hl-6-73">73</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.quarkus.devservices.common.ConfigureUtil</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.testcontainers.containers.GenericContainer</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.testcontainers.containers.wait.strategy.Wait</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.testcontainers.utility.DockerImageName</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoApiContainer</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">GenericContainer</span><span class="o">&lt;</span><span class="n">DemoApiContainer</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">CONTAINER_NAME</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">DEV_SERVICE_LABEL</span><span class="o">=</span><span class="s">&#34;quarkus-dev-service-demo&#34;</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">exposedPort</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">DEMO_INTERNAL_PORT</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">8600</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">serviceName</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">hostname</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">sharedContainer</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoApiContainer</span><span class="p">(</span><span class="n">DockerImageName</span><span class="w"> </span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">isShared</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">serviceName</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">(</span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">isShared</span><span class="p">,</span><span class="w"> </span><span class="n">serviceName</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoApiContainer</span><span class="p">(</span><span class="n">DockerImageName</span><span class="w"> </span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">exposedPort</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">isShared</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">serviceName</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">super</span><span class="p">(</span><span class="n">image</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">exposedPort</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">exposedPort</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">useSharedNetwork</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">sharedContainer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">isShared</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">serviceName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">serviceName</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">protected</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">configure</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">super</span><span class="p">.</span><span class="na">configure</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">useSharedNetwork</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">hostname</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ConfigureUtil</span><span class="p">.</span><span class="na">configureSharedNetwork</span><span class="p">(</span><span class="k">this</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">exposedPort</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addFixedExposedPort</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">exposedPort</span><span class="p">,</span><span class="w"> </span><span class="n">DEMO_INTERNAL_PORT</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">useSharedNetwork</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// expose random port for which we are able to ask Testcontainers for the actual mapped port at runtime</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// as from the host&#39;s perspective Testcontainers actually expose container ports on random host port</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addExposedPort</span><span class="p">(</span><span class="n">DEMO_INTERNAL_PORT</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addExposedPort</span><span class="p">(</span><span class="n">DEMO_INTERNAL_PORT</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">withLabel</span><span class="p">(</span><span class="n">DEV_SERVICE_LABEL</span><span class="p">,</span><span class="w"> </span><span class="n">serviceName</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Tell the dev service how to know the container is ready</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">waitingFor</span><span class="p">(</span><span class="n">Wait</span><span class="p">.</span><span class="na">forLogMessage</span><span class="p">(</span><span class="s">&#34;.*demo.*started.*&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">1</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getHost</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">useSharedNetwork</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">hostname</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kd">super</span><span class="p">.</span><span class="na">getHost</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">getPort</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">useSharedNetwork</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">DEMO_INTERNAL_PORT</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">exposedPort</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">exposedPort</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">getFirstMappedPort</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Je ne m&rsquo;attarderai pas sur cette classe qui est identique à celle de notre <code>DemoDatabaseContainer</code>. Seule l&rsquo;attente de l&rsquo;événement indiquant la fin du démarrage du processus dans le conteneur est différente. Ici, nous attendons un log dans notre application contenant les mots <code>demo</code> et <code>started</code></p> <h2 class="heading-element" id="conclusion"><span>Conclusion</span> <a href="#conclusion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Notre extension lance maintenant un service &ldquo;dockerisé&rdquo; avec sa base de données. Le tout configuré automatiquement pour fonctionner ensemble. Comme nous avions déjà inclus l&rsquo;extension lors de la précédente étape, nous n&rsquo;avons rien de plus à faire pour profiter de cette mise à jour.</p> <p>Dans un prochain article, nous verrons comment dialoguer avec notre API grâce à l&rsquo;URL sauvegardée dans notre <code>RunningDevService</code> et comment fournir un SDK permettant d&rsquo;exploiter cette API via une simple injection de dépendances dans notre application cliente.</p> Quarkus Extension Dev Service Personnalisé - Partie I https://techland.info/posts/quarkus/quarkus-extension-custom-dev-service-part-1/ Tue, 08 Oct 2024 22:34:03 +0200[email protected] (LE BARO Romain) https://techland.info/posts/quarkus/quarkus-extension-custom-dev-service-part-1/ Développement <p>Aujourd&rsquo;hui, on s&rsquo;attaque à un gros morceau avec un cas d&rsquo;usage non documenté par Quarkus, mais qui est vraiment très pratique. Quarkus propose des extensions avec des &ldquo;dev services&rdquo;. Ces extensions, une fois incluses dans votre projet, lancent des services automatiquement au démarrage du projet en mode dev. Nous allons voir comment créer notre propre &ldquo;dev service&rdquo; pour nos applications Quarkus. L&rsquo;idée est de fournir une extension pour chacun de nos services afin de les rendre utilisables facilement par des tiers.</p> <h2 class="heading-element" id="cas-dusage"><span>Cas d&rsquo;usage</span> <a href="#cas-dusage" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Imaginons une entreprise avec plusieurs projets et plusieurs équipes. Il arrive souvent que des services soient communs à plusieurs projets. Deux solutions sont généralement mises en place.</p> <ul> <li>Une instance de dev partagée</li> <li>Un docker compose à lancer en parallèle du projet en mode dev.</li> </ul> <p>La première solution implique une administration compliquée avec une gestion des données entres plusieurs équipes. Si une équipe casse le service, cela impact les autres équipes. Lors d&rsquo;une mise à jour, l&rsquo;ensemble des équipes sont également impactées.</p> <p>La seconde solution est plus flexible, les services s’exécutant sur le poste du développeur. Cependant, cela implique une étape supplémentaire au lancement du projet en mode dev. Cela pose aussi la question de la mise à jour du docker compose.</p> <p>Un &ldquo;dev service&rdquo; permet de résoudre ces problèmes. Distribuer sous la forme d&rsquo;une extension Maven, ils peuvent être maintenus à jour via l&rsquo;utilisation d&rsquo;un bom Maven. Quarkus les démarrent automatiquement au démarrage du projet, éliminant le besoin de gérer manuellement des conteneurs docker.</p> <h2 class="heading-element" id="lextension"><span>L&rsquo;extension</span> <a href="#lextension" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Notre extension devra pouvoir démarrer un service Quarkus avec sa base de données. L&rsquo;extension devra permettre d&rsquo;exécuter le service avec des paramètres par défaut et de permettre de les personnaliser.</p> <p>Commençons par créer l&rsquo;extension via la commande :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a> </span><span class="lnt" id="hl-0-3"><a class="lnlinks" href="#hl-0-3">3</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mvn io.quarkus.platform:quarkus-maven-plugin:3.14.4:create-extension -N <span class="se">\ </span></span></span><span class="line"><span class="cl"> -DgroupId<span class="o">=</span>info.techland <span class="se">\ </span></span></span><span class="line"><span class="cl"> -DextensionId<span class="o">=</span>demo-extension</span></span></code></pre></td></tr></table> </div> </div><p>Cela va nous créer une extension avec l&rsquo;arborescence suivante :</p> <pre tabindex="0"><code class="language-texte" data-lang="texte">demo-extension |__ deployment |__ integration-tests |__ runtime |__ pom.xml</code></pre><p>À la fin l&rsquo;architecture de notre extension ressemblera au schéma suivant :</p> <p><figure><img loading="lazy" src='https://techland.info/devServiceResultBuildItem/database-extension-schema.webp' alt="Schéma de notre extension pour la base de données"><figcaption class="image-caption">Schéma de notre extension pour la base de données</figcaption> </figure></p> <h2 class="heading-element" id="création-de-la-base-de-données-de-notre-dev-service"><span>Création de la base de données de notre dev service</span> <a href="#cr%c3%a9ation-de-la-base-de-donn%c3%a9es-de-notre-dev-service" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Notre service a besoin d&rsquo;une base de données pour fonctionner. Nous allons donc configurer notre extension pour lancer une base de données dans un conteneur Docker. Avant cela commençons par créer quelques configurations pour notre extension afin de la rendre personnalisable.</p> <h3 class="heading-element" id="demoextensionbuildtimeconfig"><span>DemoExtensionBuildTimeConfig</span> <a href="#demoextensionbuildtimeconfig" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a> </span><span class="lnt" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a> </span><span class="lnt" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a> </span><span class="lnt" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a> </span><span class="lnt" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a> </span><span class="lnt" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a> </span><span class="lnt" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a> </span><span class="lnt" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a> </span><span class="lnt" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a> </span><span class="lnt" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a> </span><span class="lnt" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a> </span><span class="lnt" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a> </span><span class="lnt" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a> </span><span class="lnt" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a> </span><span class="lnt" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a> </span><span class="lnt" id="hl-2-16"><a class="lnlinks" href="#hl-2-16">16</a> </span><span class="lnt" id="hl-2-17"><a class="lnlinks" href="#hl-2-17">17</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">info.techland.demo.extension.deployment</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.quarkus.runtime.annotations.ConfigPhase</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.quarkus.runtime.annotations.ConfigRoot</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.smallrye.config.ConfigMapping</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@ConfigMapping</span><span class="p">(</span><span class="n">prefix</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;quarkus.demo&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@ConfigRoot</span><span class="p">(</span><span class="n">phase</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ConfigPhase</span><span class="p">.</span><span class="na">BUILD_TIME</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">interface</span> <span class="nc">DemoExtensionBuildTimeConfig</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Dev services configuration. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">DevServicesConfig</span><span class="w"> </span><span class="nf">devservices</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Cette classe enregistre le point d&rsquo;entrée de la configuration de notre dev service. Notre configuration sera accessible dans l&rsquo;<code>application.properties</code> sous la clé <code>quarkus.demo</code>.</p> <h3 class="heading-element" id="devserviceconfig"><span>DevServiceConfig</span> <a href="#devserviceconfig" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a class="lnlinks" href="#hl-3-1"> 1</a> </span><span class="lnt" id="hl-3-2"><a class="lnlinks" href="#hl-3-2"> 2</a> </span><span class="lnt" id="hl-3-3"><a class="lnlinks" href="#hl-3-3"> 3</a> </span><span class="lnt" id="hl-3-4"><a class="lnlinks" href="#hl-3-4"> 4</a> </span><span class="lnt" id="hl-3-5"><a class="lnlinks" href="#hl-3-5"> 5</a> </span><span class="lnt" id="hl-3-6"><a class="lnlinks" href="#hl-3-6"> 6</a> </span><span class="lnt" id="hl-3-7"><a class="lnlinks" href="#hl-3-7"> 7</a> </span><span class="lnt" id="hl-3-8"><a class="lnlinks" href="#hl-3-8"> 8</a> </span><span class="lnt" id="hl-3-9"><a class="lnlinks" href="#hl-3-9"> 9</a> </span><span class="lnt" id="hl-3-10"><a class="lnlinks" href="#hl-3-10">10</a> </span><span class="lnt" id="hl-3-11"><a class="lnlinks" href="#hl-3-11">11</a> </span><span class="lnt" id="hl-3-12"><a class="lnlinks" href="#hl-3-12">12</a> </span><span class="lnt" id="hl-3-13"><a class="lnlinks" href="#hl-3-13">13</a> </span><span class="lnt" id="hl-3-14"><a class="lnlinks" href="#hl-3-14">14</a> </span><span class="lnt" id="hl-3-15"><a class="lnlinks" href="#hl-3-15">15</a> </span><span class="lnt" id="hl-3-16"><a class="lnlinks" href="#hl-3-16">16</a> </span><span class="lnt" id="hl-3-17"><a class="lnlinks" href="#hl-3-17">17</a> </span><span class="lnt" id="hl-3-18"><a class="lnlinks" href="#hl-3-18">18</a> </span><span class="lnt" id="hl-3-19"><a class="lnlinks" href="#hl-3-19">19</a> </span><span class="lnt" id="hl-3-20"><a class="lnlinks" href="#hl-3-20">20</a> </span><span class="lnt" id="hl-3-21"><a class="lnlinks" href="#hl-3-21">21</a> </span><span class="lnt" id="hl-3-22"><a class="lnlinks" href="#hl-3-22">22</a> </span><span class="lnt" id="hl-3-23"><a class="lnlinks" href="#hl-3-23">23</a> </span><span class="lnt" id="hl-3-24"><a class="lnlinks" href="#hl-3-24">24</a> </span><span class="lnt" id="hl-3-25"><a class="lnlinks" href="#hl-3-25">25</a> </span><span class="lnt" id="hl-3-26"><a class="lnlinks" href="#hl-3-26">26</a> </span><span class="lnt" id="hl-3-27"><a class="lnlinks" href="#hl-3-27">27</a> </span><span class="lnt" id="hl-3-28"><a class="lnlinks" href="#hl-3-28">28</a> </span><span class="lnt" id="hl-3-29"><a class="lnlinks" href="#hl-3-29">29</a> </span><span class="lnt" id="hl-3-30"><a class="lnlinks" href="#hl-3-30">30</a> </span><span class="lnt" id="hl-3-31"><a class="lnlinks" href="#hl-3-31">31</a> </span><span class="lnt" id="hl-3-32"><a class="lnlinks" href="#hl-3-32">32</a> </span><span class="lnt" id="hl-3-33"><a class="lnlinks" href="#hl-3-33">33</a> </span><span class="lnt" id="hl-3-34"><a class="lnlinks" href="#hl-3-34">34</a> </span><span class="lnt" id="hl-3-35"><a class="lnlinks" href="#hl-3-35">35</a> </span><span class="lnt" id="hl-3-36"><a class="lnlinks" href="#hl-3-36">36</a> </span><span class="lnt" id="hl-3-37"><a class="lnlinks" href="#hl-3-37">37</a> </span><span class="lnt" id="hl-3-38"><a class="lnlinks" href="#hl-3-38">38</a> </span><span class="lnt" id="hl-3-39"><a class="lnlinks" href="#hl-3-39">39</a> </span><span class="lnt" id="hl-3-40"><a class="lnlinks" href="#hl-3-40">40</a> </span><span class="lnt" id="hl-3-41"><a class="lnlinks" href="#hl-3-41">41</a> </span><span class="lnt" id="hl-3-42"><a class="lnlinks" href="#hl-3-42">42</a> </span><span class="lnt" id="hl-3-43"><a class="lnlinks" href="#hl-3-43">43</a> </span><span class="lnt" id="hl-3-44"><a class="lnlinks" href="#hl-3-44">44</a> </span><span class="lnt" id="hl-3-45"><a class="lnlinks" href="#hl-3-45">45</a> </span><span class="lnt" id="hl-3-46"><a class="lnlinks" href="#hl-3-46">46</a> </span><span class="lnt" id="hl-3-47"><a class="lnlinks" href="#hl-3-47">47</a> </span><span class="lnt" id="hl-3-48"><a class="lnlinks" href="#hl-3-48">48</a> </span><span class="lnt" id="hl-3-49"><a class="lnlinks" href="#hl-3-49">49</a> </span><span class="lnt" id="hl-3-50"><a class="lnlinks" href="#hl-3-50">50</a> </span><span class="lnt" id="hl-3-51"><a class="lnlinks" href="#hl-3-51">51</a> </span><span class="lnt" id="hl-3-52"><a class="lnlinks" href="#hl-3-52">52</a> </span><span class="lnt" id="hl-3-53"><a class="lnlinks" href="#hl-3-53">53</a> </span><span class="lnt" id="hl-3-54"><a class="lnlinks" href="#hl-3-54">54</a> </span><span class="lnt" id="hl-3-55"><a class="lnlinks" href="#hl-3-55">55</a> </span><span class="lnt" id="hl-3-56"><a class="lnlinks" href="#hl-3-56">56</a> </span><span class="lnt" id="hl-3-57"><a class="lnlinks" href="#hl-3-57">57</a> </span><span class="lnt" id="hl-3-58"><a class="lnlinks" href="#hl-3-58">58</a> </span><span class="lnt" id="hl-3-59"><a class="lnlinks" href="#hl-3-59">59</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">info.techland.demo.extension.deployment</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.Map</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.quarkus.runtime.annotations.ConfigGroup</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.smallrye.config.WithDefault</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@ConfigGroup</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">interface</span> <span class="nc">DevServicesConfig</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * If DevServices has been explicitly enabled or disabled. DevServices is generally enabled </span></span></span><span class="line"><span class="cl"><span class="cm"> * by default, unless there is an existing configuration present. </span></span></span><span class="line"><span class="cl"><span class="cm"> * &lt;p&gt; </span></span></span><span class="line"><span class="cl"><span class="cm"> * When DevServices is enabled Quarkus will attempt to automatically configure and start </span></span></span><span class="line"><span class="cl"><span class="cm"> * a demo instance when running in Dev or Test mode and when Docker is running. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;true&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">enabled</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Indicates if the demo server managed by Quarkus Dev Services is shared. </span></span></span><span class="line"><span class="cl"><span class="cm"> * When shared, Quarkus looks for running containers using label-based service discovery. </span></span></span><span class="line"><span class="cl"><span class="cm"> * If a matching container is found, it is used, and so a second one is not started. </span></span></span><span class="line"><span class="cl"><span class="cm"> * Otherwise, Dev Services for demo starts a new container. </span></span></span><span class="line"><span class="cl"><span class="cm"> * &lt;p&gt; </span></span></span><span class="line"><span class="cl"><span class="cm"> * The discovery uses the {@code quarkus-dev-service-redis} label. </span></span></span><span class="line"><span class="cl"><span class="cm"> * The value is configured using the {@code service-name} property. </span></span></span><span class="line"><span class="cl"><span class="cm"> * &lt;p&gt; </span></span></span><span class="line"><span class="cl"><span class="cm"> * Container sharing is only used in dev mode. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;true&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">shared</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The value of the {@code quarkus-dev-service-demo} label attached to the started container. </span></span></span><span class="line"><span class="cl"><span class="cm"> * This property is used when {@code shared} is set to {@code true}. </span></span></span><span class="line"><span class="cl"><span class="cm"> * In this case, before starting a container, Dev Services for demo looks for a container with the </span></span></span><span class="line"><span class="cl"><span class="cm"> * {@code quarkus-dev-service-demo} label </span></span></span><span class="line"><span class="cl"><span class="cm"> * set to the configured value. If found, it will use this container instead of starting a new one. Otherwise, it </span></span></span><span class="line"><span class="cl"><span class="cm"> * starts a new container with the {@code quarkus-dev-service-demo} label set to the specified value. </span></span></span><span class="line"><span class="cl"><span class="cm"> * &lt;p&gt; </span></span></span><span class="line"><span class="cl"><span class="cm"> * This property is used when you need multiple shared demo servers. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;demo&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">serviceName</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Environment variables that are passed to the container. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">containerEnv</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Configuration for the postgresql database use by demo service. </span></span></span><span class="line"><span class="cl"><span class="cm"> * @return The database configuration </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">DatabaseConfig</span><span class="w"> </span><span class="nf">db</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Cette classe configure notre dev service avec l&rsquo;accès à la configuration de notre base de données du dev service. La configuration de notre base de données sera accessible sous la clé <code>quarkus.dev.db</code>. Chaque entrée à une valeur par défaut afin de permettre l&rsquo;utilisation de l&rsquo;extension sans configuration particulière.</p> <h3 class="heading-element" id="databaseconfig"><span>DatabaseConfig</span> <a href="#databaseconfig" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a class="lnlinks" href="#hl-4-1"> 1</a> </span><span class="lnt" id="hl-4-2"><a class="lnlinks" href="#hl-4-2"> 2</a> </span><span class="lnt" id="hl-4-3"><a class="lnlinks" href="#hl-4-3"> 3</a> </span><span class="lnt" id="hl-4-4"><a class="lnlinks" href="#hl-4-4"> 4</a> </span><span class="lnt" id="hl-4-5"><a class="lnlinks" href="#hl-4-5"> 5</a> </span><span class="lnt" id="hl-4-6"><a class="lnlinks" href="#hl-4-6"> 6</a> </span><span class="lnt" id="hl-4-7"><a class="lnlinks" href="#hl-4-7"> 7</a> </span><span class="lnt" id="hl-4-8"><a class="lnlinks" href="#hl-4-8"> 8</a> </span><span class="lnt" id="hl-4-9"><a class="lnlinks" href="#hl-4-9"> 9</a> </span><span class="lnt" id="hl-4-10"><a class="lnlinks" href="#hl-4-10">10</a> </span><span class="lnt" id="hl-4-11"><a class="lnlinks" href="#hl-4-11">11</a> </span><span class="lnt" id="hl-4-12"><a class="lnlinks" href="#hl-4-12">12</a> </span><span class="lnt" id="hl-4-13"><a class="lnlinks" href="#hl-4-13">13</a> </span><span class="lnt" id="hl-4-14"><a class="lnlinks" href="#hl-4-14">14</a> </span><span class="lnt" id="hl-4-15"><a class="lnlinks" href="#hl-4-15">15</a> </span><span class="lnt" id="hl-4-16"><a class="lnlinks" href="#hl-4-16">16</a> </span><span class="lnt" id="hl-4-17"><a class="lnlinks" href="#hl-4-17">17</a> </span><span class="lnt" id="hl-4-18"><a class="lnlinks" href="#hl-4-18">18</a> </span><span class="lnt" id="hl-4-19"><a class="lnlinks" href="#hl-4-19">19</a> </span><span class="lnt" id="hl-4-20"><a class="lnlinks" href="#hl-4-20">20</a> </span><span class="lnt" id="hl-4-21"><a class="lnlinks" href="#hl-4-21">21</a> </span><span class="lnt" id="hl-4-22"><a class="lnlinks" href="#hl-4-22">22</a> </span><span class="lnt" id="hl-4-23"><a class="lnlinks" href="#hl-4-23">23</a> </span><span class="lnt" id="hl-4-24"><a class="lnlinks" href="#hl-4-24">24</a> </span><span class="lnt" id="hl-4-25"><a class="lnlinks" href="#hl-4-25">25</a> </span><span class="lnt" id="hl-4-26"><a class="lnlinks" href="#hl-4-26">26</a> </span><span class="lnt" id="hl-4-27"><a class="lnlinks" href="#hl-4-27">27</a> </span><span class="lnt" id="hl-4-28"><a class="lnlinks" href="#hl-4-28">28</a> </span><span class="lnt" id="hl-4-29"><a class="lnlinks" href="#hl-4-29">29</a> </span><span class="lnt" id="hl-4-30"><a class="lnlinks" href="#hl-4-30">30</a> </span><span class="lnt" id="hl-4-31"><a class="lnlinks" href="#hl-4-31">31</a> </span><span class="lnt" id="hl-4-32"><a class="lnlinks" href="#hl-4-32">32</a> </span><span class="lnt" id="hl-4-33"><a class="lnlinks" href="#hl-4-33">33</a> </span><span class="lnt" id="hl-4-34"><a class="lnlinks" href="#hl-4-34">34</a> </span><span class="lnt" id="hl-4-35"><a class="lnlinks" href="#hl-4-35">35</a> </span><span class="lnt" id="hl-4-36"><a class="lnlinks" href="#hl-4-36">36</a> </span><span class="lnt" id="hl-4-37"><a class="lnlinks" href="#hl-4-37">37</a> </span><span class="lnt" id="hl-4-38"><a class="lnlinks" href="#hl-4-38">38</a> </span><span class="lnt" id="hl-4-39"><a class="lnlinks" href="#hl-4-39">39</a> </span><span class="lnt" id="hl-4-40"><a class="lnlinks" href="#hl-4-40">40</a> </span><span class="lnt" id="hl-4-41"><a class="lnlinks" href="#hl-4-41">41</a> </span><span class="lnt" id="hl-4-42"><a class="lnlinks" href="#hl-4-42">42</a> </span><span class="lnt" id="hl-4-43"><a class="lnlinks" href="#hl-4-43">43</a> </span><span class="lnt" id="hl-4-44"><a class="lnlinks" href="#hl-4-44">44</a> </span><span class="lnt" id="hl-4-45"><a class="lnlinks" href="#hl-4-45">45</a> </span><span class="lnt" id="hl-4-46"><a class="lnlinks" href="#hl-4-46">46</a> </span><span class="lnt" id="hl-4-47"><a class="lnlinks" href="#hl-4-47">47</a> </span><span class="lnt" id="hl-4-48"><a class="lnlinks" href="#hl-4-48">48</a> </span><span class="lnt" id="hl-4-49"><a class="lnlinks" href="#hl-4-49">49</a> </span><span class="lnt" id="hl-4-50"><a class="lnlinks" href="#hl-4-50">50</a> </span><span class="lnt" id="hl-4-51"><a class="lnlinks" href="#hl-4-51">51</a> </span><span class="lnt" id="hl-4-52"><a class="lnlinks" href="#hl-4-52">52</a> </span><span class="lnt" id="hl-4-53"><a class="lnlinks" href="#hl-4-53">53</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">info.techland.demo.extension.deployment</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.quarkus.runtime.annotations.ConfigGroup</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.smallrye.config.WithDefault</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.Map</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.OptionalInt</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nd">@ConfigGroup</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">interface</span> <span class="nc">DatabaseConfig</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The database password used by the demo service. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;demo&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">username</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The database password used by the demo service. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;demo&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">password</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The database name used by the demo service </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;demo&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">name</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The number of reactive connection to use. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;20&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">reactiveMaxSize</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The port used by the database. </span></span></span><span class="line"><span class="cl"><span class="cm"> * @return The database port </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">OptionalInt</span><span class="w"> </span><span class="nf">port</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Name of the postgresql image to use to start the database </span></span></span><span class="line"><span class="cl"><span class="cm"> * @return The image name. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@WithDefault</span><span class="p">(</span><span class="s">&#34;docker.io/postgres&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">imageName</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Environment variables that are passed to the container. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">containerEnv</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>La configuration de la base de données contient les informations de connexion à la base de données PostgreSQL utilisée par défaut. Encore une fois, nous privilégions les conventions sur la configuration en appliquant des valeurs par défaut.</p> <h3 class="heading-element" id="demoextensionprocessor"><span>DemoExtensionProcessor</span> <a href="#demoextensionprocessor" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>Pour demander à Quarkus de démarrer un conteneur, nous allons devoir configurer notre <code>DemoExtensionProcessor</code>. Commençons par ajouter une méthode <code>createContainer</code> qui fera office de <code>@BuildStep</code> pour démarrer et configurer notre conteneur.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a class="lnlinks" href="#hl-5-1"> 1</a> </span><span class="lnt" id="hl-5-2"><a class="lnlinks" href="#hl-5-2"> 2</a> </span><span class="lnt" id="hl-5-3"><a class="lnlinks" href="#hl-5-3"> 3</a> </span><span class="lnt" id="hl-5-4"><a class="lnlinks" href="#hl-5-4"> 4</a> </span><span class="lnt" id="hl-5-5"><a class="lnlinks" href="#hl-5-5"> 5</a> </span><span class="lnt" id="hl-5-6"><a class="lnlinks" href="#hl-5-6"> 6</a> </span><span class="lnt" id="hl-5-7"><a class="lnlinks" href="#hl-5-7"> 7</a> </span><span class="lnt" id="hl-5-8"><a class="lnlinks" href="#hl-5-8"> 8</a> </span><span class="lnt" id="hl-5-9"><a class="lnlinks" href="#hl-5-9"> 9</a> </span><span class="lnt" id="hl-5-10"><a class="lnlinks" href="#hl-5-10">10</a> </span><span class="lnt" id="hl-5-11"><a class="lnlinks" href="#hl-5-11">11</a> </span><span class="lnt" id="hl-5-12"><a class="lnlinks" href="#hl-5-12">12</a> </span><span class="lnt" id="hl-5-13"><a class="lnlinks" href="#hl-5-13">13</a> </span><span class="lnt" id="hl-5-14"><a class="lnlinks" href="#hl-5-14">14</a> </span><span class="lnt" id="hl-5-15"><a class="lnlinks" href="#hl-5-15">15</a> </span><span class="lnt" id="hl-5-16"><a class="lnlinks" href="#hl-5-16">16</a> </span><span class="lnt" id="hl-5-17"><a class="lnlinks" href="#hl-5-17">17</a> </span><span class="lnt" id="hl-5-18"><a class="lnlinks" href="#hl-5-18">18</a> </span><span class="lnt" id="hl-5-19"><a class="lnlinks" href="#hl-5-19">19</a> </span><span class="lnt" id="hl-5-20"><a class="lnlinks" href="#hl-5-20">20</a> </span><span class="lnt" id="hl-5-21"><a class="lnlinks" href="#hl-5-21">21</a> </span><span class="lnt" id="hl-5-22"><a class="lnlinks" href="#hl-5-22">22</a> </span><span class="lnt" id="hl-5-23"><a class="lnlinks" href="#hl-5-23">23</a> </span><span class="lnt" id="hl-5-24"><a class="lnlinks" href="#hl-5-24">24</a> </span><span class="lnt" id="hl-5-25"><a class="lnlinks" href="#hl-5-25">25</a> </span><span class="lnt" id="hl-5-26"><a class="lnlinks" href="#hl-5-26">26</a> </span><span class="lnt" id="hl-5-27"><a class="lnlinks" href="#hl-5-27">27</a> </span><span class="lnt" id="hl-5-28"><a class="lnlinks" href="#hl-5-28">28</a> </span><span class="lnt" id="hl-5-29"><a class="lnlinks" href="#hl-5-29">29</a> </span><span class="lnt" id="hl-5-30"><a class="lnlinks" href="#hl-5-30">30</a> </span><span class="lnt" id="hl-5-31"><a class="lnlinks" href="#hl-5-31">31</a> </span><span class="lnt" id="hl-5-32"><a class="lnlinks" href="#hl-5-32">32</a> </span><span class="lnt" id="hl-5-33"><a class="lnlinks" href="#hl-5-33">33</a> </span><span class="lnt" id="hl-5-34"><a class="lnlinks" href="#hl-5-34">34</a> </span><span class="lnt" id="hl-5-35"><a class="lnlinks" href="#hl-5-35">35</a> </span><span class="lnt" id="hl-5-36"><a class="lnlinks" href="#hl-5-36">36</a> </span><span class="lnt" id="hl-5-37"><a class="lnlinks" href="#hl-5-37">37</a> </span><span class="lnt" id="hl-5-38"><a class="lnlinks" href="#hl-5-38">38</a> </span><span class="lnt" id="hl-5-39"><a class="lnlinks" href="#hl-5-39">39</a> </span><span class="lnt" id="hl-5-40"><a class="lnlinks" href="#hl-5-40">40</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildSteps</span><span class="p">(</span><span class="n">onlyIf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="n">GlobalDevServicesConfig</span><span class="p">.</span><span class="na">Enabled</span><span class="p">.</span><span class="na">class</span><span class="p">})</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoExtensionProcessor</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The current configuration. either from running container instance or an instance that is about to start. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">volatile</span><span class="w"> </span><span class="n">DevServicesConfig</span><span class="w"> </span><span class="n">runningConfiguration</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">volatile</span><span class="w"> </span><span class="n">RunningDevService</span><span class="w"> </span><span class="n">demoDatabaseDevService</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildStep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">createContainer</span><span class="p">(</span><span class="n">DockerStatusBuildItem</span><span class="w"> </span><span class="n">dockerStatusBuildItem</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">DevServicesSharedNetworkBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">devServicesSharedNetworkBuildItem</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">DemoBuildTimeConfig</span><span class="w"> </span><span class="n">buildTimeConfig</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">GlobalDevServicesConfig</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">DevServicesResultBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">devServicesResult</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">runningConfiguration</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">buildTimeConfig</span><span class="p">.</span><span class="na">devservices</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">errors</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrayList</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">DemoDatabaseRunningDevService</span><span class="w"> </span><span class="n">newDatabaseDevService</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">startDatabaseContainer</span><span class="p">(</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">dockerStatusBuildItem</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">!</span><span class="n">devServicesSharedNetworkBuildItem</span><span class="p">.</span><span class="na">isEmpty</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">timeout</span><span class="p">.</span><span class="na">orElse</span><span class="p">(</span><span class="n">Duration</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">0</span><span class="p">,</span><span class="w"> </span><span class="n">ChronoUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">)),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">runningConfiguration</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">errors</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Get the RunningDevService instance.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">demoDatabaseDevService</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">newDatabaseDevService</span><span class="p">.</span><span class="na">getRunningDevService</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">Throwable</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RuntimeException</span><span class="p">(</span><span class="n">t</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Log</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">&#34;Dev Services for Demo started.&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Call to toBuildItem method to get the DevServicesResultBuildItem need by the BuildStep producer.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">devServicesResult</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="n">demoDatabaseDevService</span><span class="p">.</span><span class="na">toBuildItem</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Notre méthode se voit injecter les builds items suivants :</p> <ul> <li><strong>DockerStatusBuildItem</strong> : Permet de récupérer le statut d&rsquo;une instance docker</li> <li><strong>DevServicesSharedNetworkBuildItem</strong> : Détermine si les conteneurs sont démarrés sur un réseau partagé</li> <li><strong>DemoBuildTimeConfig</strong> : La configuration de notre extension</li> <li><strong>GlobalDevServicesConfig</strong> : La configuration globale des devservices fournie par Quarkus</li> <li><strong>DevServicesResultBuildItem</strong> : Le buildItem à produire pour référencer notre conteneur auprès de Quarkus</li> </ul> <p>Notre méthode appelle ensuite la méthode <code>startDatabaseContainer</code> chargée de créer une instance de <code>DemoDatabaseRunningDevService</code>. Cette dernière est en charge de fournir un <code>RunningDevService</code>, via <code>devServicesResult.produce(demoDatabaseDevService.toBuildItem())</code>, qui sera retourné par notre <code>@BuildStep</code> pour demander à Quarkus de lancer un conteneur.</p> <h4 class="heading-element" id="startdatabasecontainer"><span>startDatabaseContainer</span> <a href="#startdatabasecontainer" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h4><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a class="lnlinks" href="#hl-6-1"> 1</a> </span><span class="lnt" id="hl-6-2"><a class="lnlinks" href="#hl-6-2"> 2</a> </span><span class="lnt" id="hl-6-3"><a class="lnlinks" href="#hl-6-3"> 3</a> </span><span class="lnt" id="hl-6-4"><a class="lnlinks" href="#hl-6-4"> 4</a> </span><span class="lnt" id="hl-6-5"><a class="lnlinks" href="#hl-6-5"> 5</a> </span><span class="lnt" id="hl-6-6"><a class="lnlinks" href="#hl-6-6"> 6</a> </span><span class="lnt" id="hl-6-7"><a class="lnlinks" href="#hl-6-7"> 7</a> </span><span class="lnt" id="hl-6-8"><a class="lnlinks" href="#hl-6-8"> 8</a> </span><span class="lnt" id="hl-6-9"><a class="lnlinks" href="#hl-6-9"> 9</a> </span><span class="lnt" id="hl-6-10"><a class="lnlinks" href="#hl-6-10">10</a> </span><span class="lnt" id="hl-6-11"><a class="lnlinks" href="#hl-6-11">11</a> </span><span class="lnt" id="hl-6-12"><a class="lnlinks" href="#hl-6-12">12</a> </span><span class="lnt" id="hl-6-13"><a class="lnlinks" href="#hl-6-13">13</a> </span><span class="lnt" id="hl-6-14"><a class="lnlinks" href="#hl-6-14">14</a> </span><span class="lnt" id="hl-6-15"><a class="lnlinks" href="#hl-6-15">15</a> </span><span class="lnt" id="hl-6-16"><a class="lnlinks" href="#hl-6-16">16</a> </span><span class="lnt" id="hl-6-17"><a class="lnlinks" href="#hl-6-17">17</a> </span><span class="lnt" id="hl-6-18"><a class="lnlinks" href="#hl-6-18">18</a> </span><span class="lnt" id="hl-6-19"><a class="lnlinks" href="#hl-6-19">19</a> </span><span class="lnt" id="hl-6-20"><a class="lnlinks" href="#hl-6-20">20</a> </span><span class="lnt" id="hl-6-21"><a class="lnlinks" href="#hl-6-21">21</a> </span><span class="lnt" id="hl-6-22"><a class="lnlinks" href="#hl-6-22">22</a> </span><span class="lnt" id="hl-6-23"><a class="lnlinks" href="#hl-6-23">23</a> </span><span class="lnt" id="hl-6-24"><a class="lnlinks" href="#hl-6-24">24</a> </span><span class="lnt" id="hl-6-25"><a class="lnlinks" href="#hl-6-25">25</a> </span><span class="lnt" id="hl-6-26"><a class="lnlinks" href="#hl-6-26">26</a> </span><span class="lnt" id="hl-6-27"><a class="lnlinks" href="#hl-6-27">27</a> </span><span class="lnt" id="hl-6-28"><a class="lnlinks" href="#hl-6-28">28</a> </span><span class="lnt" id="hl-6-29"><a class="lnlinks" href="#hl-6-29">29</a> </span><span class="lnt" id="hl-6-30"><a class="lnlinks" href="#hl-6-30">30</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">private</span><span class="w"> </span><span class="n">DemoDatabaseRunningDevService</span><span class="w"> </span><span class="nf">startDatabaseContainer</span><span class="p">(</span><span class="n">DockerStatusBuildItem</span><span class="w"> </span><span class="n">dockerStatusBuildItem</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">Duration</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">DevServicesConfig</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">errors</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">enabled</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// explicitly disabled</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Log</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">&#34;Not starting Dev Services for Demo as it has been disabled in the config&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">dockerStatusBuildItem</span><span class="p">.</span><span class="na">isDockerAvailable</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Log</span><span class="p">.</span><span class="na">warn</span><span class="p">(</span><span class="s">&#34;Please get a working docker instance&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">errors</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="s">&#34;Please get a working docker instance&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">ContainerAddress</span><span class="o">&gt;</span><span class="w"> </span><span class="n">maybeExistingContainer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">demoDBDevModeContainerLocator</span><span class="p">.</span><span class="na">locateContainer</span><span class="p">(</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">serviceName</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;_db&#34;</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">shared</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">LaunchMode</span><span class="p">.</span><span class="na">current</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Get the image name from the configuration.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">imageName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">imageName</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">DockerImageName</span><span class="w"> </span><span class="n">dockerImageName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">DockerImageName</span><span class="p">.</span><span class="na">parse</span><span class="p">(</span><span class="n">imageName</span><span class="p">).</span><span class="na">asCompatibleSubstituteFor</span><span class="p">(</span><span class="n">imageName</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">maybeExistingContainer</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">map</span><span class="p">(</span><span class="n">existingContainer</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DemoDatabaseRunningDevService</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">RunningDevService</span><span class="p">(</span><span class="n">DemoDatabaseContainer</span><span class="p">.</span><span class="na">CONTAINER_NAME</span><span class="p">,</span><span class="w"> </span><span class="n">existingContainer</span><span class="p">.</span><span class="na">getId</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="s">&#34;url&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">existingContainer</span><span class="p">.</span><span class="na">getHost</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;:&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">existingContainer</span><span class="p">.</span><span class="na">getPort</span><span class="p">()))))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">orElseGet</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DemoDatabaseRunningDevService</span><span class="p">(</span><span class="n">DemoDatabaseContainer</span><span class="p">.</span><span class="na">CONTAINER_NAME</span><span class="p">,</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">serviceName</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;_db&#34;</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>On commence par quelques vérifications. Si les devservices sont désactivés de manière globale, on annule le lancement et si docker n&rsquo;est pas disponible, on retourne une erreur. La méthode vérifie ensuite si un autre conteneur existe déjà. Cela peut être le cas, si deux services Quarkus utilise la même extension. Cela se fait grâce à un <code>ContainerLocator</code>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-7-1"><a class="lnlinks" href="#hl-7-1">1</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">ContainerLocator</span><span class="w"> </span><span class="n">demoDBDevModeContainerLocator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ContainerLocator</span><span class="p">(</span><span class="n">DemoDatabaseContainer</span><span class="p">.</span><span class="na">DEV_SERVICE_LABEL</span><span class="p">,</span><span class="w"> </span><span class="n">POSTGRES_INTERNAL_PORT</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div><p>Si un conteneur de notre base de données est déjà lancé, alors la méthode retourne un <code>RunningDevService</code> avec les informations du conteneur existant. Sinon un nouveau <code>RunningDevService</code> pour le nouveau conteneur est retourné.</p> <h4 class="heading-element" id="demodatabaserunningdevservice"><span>DemoDatabaseRunningDevService</span> <a href="#demodatabaserunningdevservice" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h4><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-8-1"><a class="lnlinks" href="#hl-8-1"> 1</a> </span><span class="lnt" id="hl-8-2"><a class="lnlinks" href="#hl-8-2"> 2</a> </span><span class="lnt" id="hl-8-3"><a class="lnlinks" href="#hl-8-3"> 3</a> </span><span class="lnt" id="hl-8-4"><a class="lnlinks" href="#hl-8-4"> 4</a> </span><span class="lnt" id="hl-8-5"><a class="lnlinks" href="#hl-8-5"> 5</a> </span><span class="lnt" id="hl-8-6"><a class="lnlinks" href="#hl-8-6"> 6</a> </span><span class="lnt" id="hl-8-7"><a class="lnlinks" href="#hl-8-7"> 7</a> </span><span class="lnt" id="hl-8-8"><a class="lnlinks" href="#hl-8-8"> 8</a> </span><span class="lnt" id="hl-8-9"><a class="lnlinks" href="#hl-8-9"> 9</a> </span><span class="lnt" id="hl-8-10"><a class="lnlinks" href="#hl-8-10">10</a> </span><span class="lnt" id="hl-8-11"><a class="lnlinks" href="#hl-8-11">11</a> </span><span class="lnt" id="hl-8-12"><a class="lnlinks" href="#hl-8-12">12</a> </span><span class="lnt" id="hl-8-13"><a class="lnlinks" href="#hl-8-13">13</a> </span><span class="lnt" id="hl-8-14"><a class="lnlinks" href="#hl-8-14">14</a> </span><span class="lnt" id="hl-8-15"><a class="lnlinks" href="#hl-8-15">15</a> </span><span class="lnt" id="hl-8-16"><a class="lnlinks" href="#hl-8-16">16</a> </span><span class="lnt" id="hl-8-17"><a class="lnlinks" href="#hl-8-17">17</a> </span><span class="lnt" id="hl-8-18"><a class="lnlinks" href="#hl-8-18">18</a> </span><span class="lnt" id="hl-8-19"><a class="lnlinks" href="#hl-8-19">19</a> </span><span class="lnt" id="hl-8-20"><a class="lnlinks" href="#hl-8-20">20</a> </span><span class="lnt" id="hl-8-21"><a class="lnlinks" href="#hl-8-21">21</a> </span><span class="lnt" id="hl-8-22"><a class="lnlinks" href="#hl-8-22">22</a> </span><span class="lnt" id="hl-8-23"><a class="lnlinks" href="#hl-8-23">23</a> </span><span class="lnt" id="hl-8-24"><a class="lnlinks" href="#hl-8-24">24</a> </span><span class="lnt" id="hl-8-25"><a class="lnlinks" href="#hl-8-25">25</a> </span><span class="lnt" id="hl-8-26"><a class="lnlinks" href="#hl-8-26">26</a> </span><span class="lnt" id="hl-8-27"><a class="lnlinks" href="#hl-8-27">27</a> </span><span class="lnt" id="hl-8-28"><a class="lnlinks" href="#hl-8-28">28</a> </span><span class="lnt" id="hl-8-29"><a class="lnlinks" href="#hl-8-29">29</a> </span><span class="lnt" id="hl-8-30"><a class="lnlinks" href="#hl-8-30">30</a> </span><span class="lnt" id="hl-8-31"><a class="lnlinks" href="#hl-8-31">31</a> </span><span class="lnt" id="hl-8-32"><a class="lnlinks" href="#hl-8-32">32</a> </span><span class="lnt" id="hl-8-33"><a class="lnlinks" href="#hl-8-33">33</a> </span><span class="lnt" id="hl-8-34"><a class="lnlinks" href="#hl-8-34">34</a> </span><span class="lnt" id="hl-8-35"><a class="lnlinks" href="#hl-8-35">35</a> </span><span class="lnt" id="hl-8-36"><a class="lnlinks" href="#hl-8-36">36</a> </span><span class="lnt" id="hl-8-37"><a class="lnlinks" href="#hl-8-37">37</a> </span><span class="lnt" id="hl-8-38"><a class="lnlinks" href="#hl-8-38">38</a> </span><span class="lnt" id="hl-8-39"><a class="lnlinks" href="#hl-8-39">39</a> </span><span class="lnt" id="hl-8-40"><a class="lnlinks" href="#hl-8-40">40</a> </span><span class="lnt" id="hl-8-41"><a class="lnlinks" href="#hl-8-41">41</a> </span><span class="lnt" id="hl-8-42"><a class="lnlinks" href="#hl-8-42">42</a> </span><span class="lnt" id="hl-8-43"><a class="lnlinks" href="#hl-8-43">43</a> </span><span class="lnt" id="hl-8-44"><a class="lnlinks" href="#hl-8-44">44</a> </span><span class="lnt" id="hl-8-45"><a class="lnlinks" href="#hl-8-45">45</a> </span><span class="lnt" id="hl-8-46"><a class="lnlinks" href="#hl-8-46">46</a> </span><span class="lnt" id="hl-8-47"><a class="lnlinks" href="#hl-8-47">47</a> </span><span class="lnt" id="hl-8-48"><a class="lnlinks" href="#hl-8-48">48</a> </span><span class="lnt" id="hl-8-49"><a class="lnlinks" href="#hl-8-49">49</a> </span><span class="lnt" id="hl-8-50"><a class="lnlinks" href="#hl-8-50">50</a> </span><span class="lnt" id="hl-8-51"><a class="lnlinks" href="#hl-8-51">51</a> </span><span class="lnt" id="hl-8-52"><a class="lnlinks" href="#hl-8-52">52</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoDatabaseRunningDevService</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">DemoDatabaseContainer</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="w"> </span><span class="n">runningDevServiceSupplier</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoDatabaseRunningDevService</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">feature</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">Duration</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">DevServicesConfig</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span><span class="n">DockerImageName</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">serviceName</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">runningDevServiceSupplier</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">run</span><span class="p">(</span><span class="n">feature</span><span class="p">,</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">serviceName</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoDatabaseRunningDevService</span><span class="p">(</span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="w"> </span><span class="n">runningDevService</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">runningDevServiceSupplier</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">runningDevService</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="w"> </span><span class="nf">run</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">feature</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">Duration</span><span class="w"> </span><span class="n">timeout</span><span class="p">,</span><span class="w"> </span><span class="n">DevServicesConfig</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">,</span><span class="w"> </span><span class="n">DockerImageName</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">serviceName</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">port</span><span class="p">().</span><span class="na">isPresent</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DemoDatabaseContainer</span><span class="p">(</span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">port</span><span class="p">().</span><span class="na">getAsInt</span><span class="p">(),</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">serviceName</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DemoDatabaseContainer</span><span class="p">(</span><span class="n">dockerImageName</span><span class="p">,</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">serviceName</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">timeout</span><span class="p">).</span><span class="na">ifPresent</span><span class="p">(</span><span class="n">databaseContainer</span><span class="p">::</span><span class="n">withStartupTimeout</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">withEnv</span><span class="p">(</span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">containerEnv</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">withEnv</span><span class="p">(</span><span class="s">&#34;POSTGRES_USER&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">username</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">withEnv</span><span class="p">(</span><span class="s">&#34;POSTGRES_PASSWORD&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">password</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">withEnv</span><span class="p">(</span><span class="s">&#34;POSTGRES_DB&#34;</span><span class="p">,</span><span class="n">devServicesConfig</span><span class="p">.</span><span class="na">db</span><span class="p">().</span><span class="na">name</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">withNetwork</span><span class="p">(</span><span class="n">useSharedNetwork</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="n">Network</span><span class="p">.</span><span class="na">SHARED</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">Network</span><span class="p">.</span><span class="na">newNetwork</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">start</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="p">(</span><span class="n">feature</span><span class="p">,</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">getContainerId</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">databaseContainer</span><span class="p">::</span><span class="n">close</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">&#34;url&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">getHost</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;:&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">getPort</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="s">&#34;url-internal&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">databaseContainer</span><span class="p">.</span><span class="na">getContainerName</span><span class="p">().</span><span class="na">substring</span><span class="p">(</span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;:&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">POSTGRES_INTERNAL_PORT</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">GenericContainer</span><span class="o">&lt;</span><span class="n">DemoDatabaseContainer</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="nf">getDatabaseContainer</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">databaseContainer</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">DevServicesResultBuildItem</span><span class="p">.</span><span class="na">RunningDevService</span><span class="w"> </span><span class="nf">getRunningDevService</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">runningDevServiceSupplier</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Notre classe définit deux constructeurs. Un renvoyant juste le <code>RunningDevService</code> existant dans le cas où le conteneur existe déjà. Le deuxième, créant un nouveau <code>RunningDevService</code>. Notre méthode <code>run</code> commence par vérifier si un port est précisé dans la configuration du dev-service. Si c&rsquo;est le cas, notre conteneur sera démarré sur ce port, sinon, un port aléatoire disponible sera alloué (comportement par défaut).</p> <p>Une fois l&rsquo;instance de notre <code>DemoDatabaseConteneur</code> créée, nous le configurons en lui passant les variables d&rsquo;environnement qu&rsquo;il attend grâce à la méthode <code>withEnv</code>. Vient ensuite la configuration du réseau docker utilisé. Dans le cas où l&rsquo;utilisateur à expressement spécifié que les conteneurs devait être attachés au réseau partagé créé par TestContainer, nous utilisons la méthode <code>withNetwork</code> avec la valeur <code>Network.SHARED</code>. Dans le cas contraire, un nouveau réseau est créé pour le conteneur.</p> <p>Enfin, nous retournons notre <code>RunninDevService</code> contenant l&rsquo;ID de notre conteneur ainsi que de la configuration qui nous sera utile par la suite.</p> <h4 class="heading-element" id="demodatabasecontainer"><span>DemoDatabaseContainer</span> <a href="#demodatabasecontainer" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h4><p>Il ne nous reste plus qu&rsquo;à voir la classe <code>DemoDatabaseContainer</code> pour finir ce premier post sur la création d&rsquo;un DevService avec Quarkus.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-9-1"><a class="lnlinks" href="#hl-9-1"> 1</a> </span><span class="lnt" id="hl-9-2"><a class="lnlinks" href="#hl-9-2"> 2</a> </span><span class="lnt" id="hl-9-3"><a class="lnlinks" href="#hl-9-3"> 3</a> </span><span class="lnt" id="hl-9-4"><a class="lnlinks" href="#hl-9-4"> 4</a> </span><span class="lnt" id="hl-9-5"><a class="lnlinks" href="#hl-9-5"> 5</a> </span><span class="lnt" id="hl-9-6"><a class="lnlinks" href="#hl-9-6"> 6</a> </span><span class="lnt" id="hl-9-7"><a class="lnlinks" href="#hl-9-7"> 7</a> </span><span class="lnt" id="hl-9-8"><a class="lnlinks" href="#hl-9-8"> 8</a> </span><span class="lnt" id="hl-9-9"><a class="lnlinks" href="#hl-9-9"> 9</a> </span><span class="lnt" id="hl-9-10"><a class="lnlinks" href="#hl-9-10">10</a> </span><span class="lnt" id="hl-9-11"><a class="lnlinks" href="#hl-9-11">11</a> </span><span class="lnt" id="hl-9-12"><a class="lnlinks" href="#hl-9-12">12</a> </span><span class="lnt" id="hl-9-13"><a class="lnlinks" href="#hl-9-13">13</a> </span><span class="lnt" id="hl-9-14"><a class="lnlinks" href="#hl-9-14">14</a> </span><span class="lnt" id="hl-9-15"><a class="lnlinks" href="#hl-9-15">15</a> </span><span class="lnt" id="hl-9-16"><a class="lnlinks" href="#hl-9-16">16</a> </span><span class="lnt" id="hl-9-17"><a class="lnlinks" href="#hl-9-17">17</a> </span><span class="lnt" id="hl-9-18"><a class="lnlinks" href="#hl-9-18">18</a> </span><span class="lnt" id="hl-9-19"><a class="lnlinks" href="#hl-9-19">19</a> </span><span class="lnt" id="hl-9-20"><a class="lnlinks" href="#hl-9-20">20</a> </span><span class="lnt" id="hl-9-21"><a class="lnlinks" href="#hl-9-21">21</a> </span><span class="lnt" id="hl-9-22"><a class="lnlinks" href="#hl-9-22">22</a> </span><span class="lnt" id="hl-9-23"><a class="lnlinks" href="#hl-9-23">23</a> </span><span class="lnt" id="hl-9-24"><a class="lnlinks" href="#hl-9-24">24</a> </span><span class="lnt" id="hl-9-25"><a class="lnlinks" href="#hl-9-25">25</a> </span><span class="lnt" id="hl-9-26"><a class="lnlinks" href="#hl-9-26">26</a> </span><span class="lnt" id="hl-9-27"><a class="lnlinks" href="#hl-9-27">27</a> </span><span class="lnt" id="hl-9-28"><a class="lnlinks" href="#hl-9-28">28</a> </span><span class="lnt" id="hl-9-29"><a class="lnlinks" href="#hl-9-29">29</a> </span><span class="lnt" id="hl-9-30"><a class="lnlinks" href="#hl-9-30">30</a> </span><span class="lnt" id="hl-9-31"><a class="lnlinks" href="#hl-9-31">31</a> </span><span class="lnt" id="hl-9-32"><a class="lnlinks" href="#hl-9-32">32</a> </span><span class="lnt" id="hl-9-33"><a class="lnlinks" href="#hl-9-33">33</a> </span><span class="lnt" id="hl-9-34"><a class="lnlinks" href="#hl-9-34">34</a> </span><span class="lnt" id="hl-9-35"><a class="lnlinks" href="#hl-9-35">35</a> </span><span class="lnt" id="hl-9-36"><a class="lnlinks" href="#hl-9-36">36</a> </span><span class="lnt" id="hl-9-37"><a class="lnlinks" href="#hl-9-37">37</a> </span><span class="lnt" id="hl-9-38"><a class="lnlinks" href="#hl-9-38">38</a> </span><span class="lnt" id="hl-9-39"><a class="lnlinks" href="#hl-9-39">39</a> </span><span class="lnt" id="hl-9-40"><a class="lnlinks" href="#hl-9-40">40</a> </span><span class="lnt" id="hl-9-41"><a class="lnlinks" href="#hl-9-41">41</a> </span><span class="lnt" id="hl-9-42"><a class="lnlinks" href="#hl-9-42">42</a> </span><span class="lnt" id="hl-9-43"><a class="lnlinks" href="#hl-9-43">43</a> </span><span class="lnt" id="hl-9-44"><a class="lnlinks" href="#hl-9-44">44</a> </span><span class="lnt" id="hl-9-45"><a class="lnlinks" href="#hl-9-45">45</a> </span><span class="lnt" id="hl-9-46"><a class="lnlinks" href="#hl-9-46">46</a> </span><span class="lnt" id="hl-9-47"><a class="lnlinks" href="#hl-9-47">47</a> </span><span class="lnt" id="hl-9-48"><a class="lnlinks" href="#hl-9-48">48</a> </span><span class="lnt" id="hl-9-49"><a class="lnlinks" href="#hl-9-49">49</a> </span><span class="lnt" id="hl-9-50"><a class="lnlinks" href="#hl-9-50">50</a> </span><span class="lnt" id="hl-9-51"><a class="lnlinks" href="#hl-9-51">51</a> </span><span class="lnt" id="hl-9-52"><a class="lnlinks" href="#hl-9-52">52</a> </span><span class="lnt" id="hl-9-53"><a class="lnlinks" href="#hl-9-53">53</a> </span><span class="lnt" id="hl-9-54"><a class="lnlinks" href="#hl-9-54">54</a> </span><span class="lnt" id="hl-9-55"><a class="lnlinks" href="#hl-9-55">55</a> </span><span class="lnt" id="hl-9-56"><a class="lnlinks" href="#hl-9-56">56</a> </span><span class="lnt" id="hl-9-57"><a class="lnlinks" href="#hl-9-57">57</a> </span><span class="lnt" id="hl-9-58"><a class="lnlinks" href="#hl-9-58">58</a> </span><span class="lnt" id="hl-9-59"><a class="lnlinks" href="#hl-9-59">59</a> </span><span class="lnt" id="hl-9-60"><a class="lnlinks" href="#hl-9-60">60</a> </span><span class="lnt" id="hl-9-61"><a class="lnlinks" href="#hl-9-61">61</a> </span><span class="lnt" id="hl-9-62"><a class="lnlinks" href="#hl-9-62">62</a> </span><span class="lnt" id="hl-9-63"><a class="lnlinks" href="#hl-9-63">63</a> </span><span class="lnt" id="hl-9-64"><a class="lnlinks" href="#hl-9-64">64</a> </span><span class="lnt" id="hl-9-65"><a class="lnlinks" href="#hl-9-65">65</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">io.quarkus.devservices.common.ConfigureUtil</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.testcontainers.containers.GenericContainer</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.testcontainers.containers.wait.strategy.Wait</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">org.testcontainers.utility.DockerImageName</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">DemoDatabaseContainer</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">GenericContainer</span><span class="o">&lt;</span><span class="n">DemoDatabaseContainer</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">CONTAINER_NAME</span><span class="o">=</span><span class="s">&#34;demo-db&#34;</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">DEV_SERVICE_LABEL</span><span class="o">=</span><span class="s">&#34;quarkus-dev-service-db&#34;</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">serviceName</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">fixedExposedPort</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">POSTGRES_INTERNAL_PORT</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">5432</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">hostname</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoDatabaseContainer</span><span class="p">(</span><span class="n">DockerImageName</span><span class="w"> </span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">serviceName</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">(</span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">serviceName</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">DemoDatabaseContainer</span><span class="p">(</span><span class="n">DockerImageName</span><span class="w"> </span><span class="n">image</span><span class="p">,</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">exposedPort</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">serviceName</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">super</span><span class="p">(</span><span class="n">image</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">fixedExposedPort</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">exposedPort</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">useSharedNetwork</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">useSharedNetwork</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">serviceName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">serviceName</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">protected</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">configure</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">super</span><span class="p">.</span><span class="na">configure</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">useSharedNetwork</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">hostname</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ConfigureUtil</span><span class="p">.</span><span class="na">configureSharedNetwork</span><span class="p">(</span><span class="k">this</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;demo&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">fixedExposedPort</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addFixedExposedPort</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">fixedExposedPort</span><span class="p">,</span><span class="w"> </span><span class="n">POSTGRES_INTERNAL_PORT</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addExposedPort</span><span class="p">(</span><span class="n">POSTGRES_INTERNAL_PORT</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">withLabel</span><span class="p">(</span><span class="n">DEV_SERVICE_LABEL</span><span class="p">,</span><span class="w"> </span><span class="n">serviceName</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">super</span><span class="p">.</span><span class="na">setWaitStrategy</span><span class="p">(</span><span class="n">Wait</span><span class="p">.</span><span class="na">forListeningPorts</span><span class="p">(</span><span class="n">POSTGRES_INTERNAL_PORT</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Override</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getHost</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">useSharedNetwork</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">hostname</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kd">super</span><span class="p">.</span><span class="na">getHost</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">getPort</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">useSharedNetwork</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">POSTGRES_INTERNAL_PORT</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">fixedExposedPort</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">fixedExposedPort</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">getFirstMappedPort</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Cette classe étend de <code>GenericContainer</code> et contient la configuration utilisée par <code>TestContainer</code> pour démarrer notre conteneur <code>Docker</code>. Dans la méthode <code>configure</code>, on commence par regarder si le conteneur doit utiliser le <code>Shared Network</code>. Si c&rsquo;est le cas <code>ConfigureUtil.configureSharedNetwork</code> se charge de tout configurer correctement. Dans le cas contraire, on configure le port (fixe ou aléatoire), on ajoute un label et on précise sur quel port écouter pour savoir quand le service a terminé de démarrer.</p> <h2 class="heading-element" id="conclusion"><span>Conclusion</span> <a href="#conclusion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Notre extension de base de données est maintenant fonctionnelle. Cette dernière fournit une implémentation d&rsquo;un <code>GénericContainer</code> au travers d&rsquo;un <code>RunningDevService</code> à notre <code>DemoExtensionProcessor</code>. Le tout est personnalisable via la configuration classique de Quarkus. Il nous suffit d&rsquo;inclure l&rsquo;extension dans une application quarkus via un import Maven pour avoir la base de données démarrée en parallèle.</p> <p>Dans un prochain article, nous verrons comment lancer un service dans son propre conteneur utilisant cette base de données pour exposer une API à notre application Quarkus.</p> Backstage oidc Provider https://techland.info/posts/backstage/oidc-provider/ Wed, 25 Sep 2024 08:40:03 +0200[email protected] (LE BARO Romain) https://techland.info/posts/backstage/oidc-provider/ Plateforme <p>Backstage est un formidable outil de tableau de bord pour les développeurs. Son système de plugin le rends particulièrement flexible. Cependant la configuration de ces plugins peut s&rsquo;avérer fastidueuse. Backstage est en constante évolution, ce qui fait que la documentation à parfois du mal à suivre. C&rsquo;est le cas depuis la migration vers le nouveau <code>Backend system</code>. Backstage dispose de plusieurs plugin permettant de se connecter à la majorité des providers d&rsquo;identité via OIDC. Toutefois, si vous souhaitez connecter backstage à un provider personnalisé comme un Keycloak d&rsquo;entreprise la documentation n&rsquo;est pas vraiment clair. C&rsquo;est ce que nous allons voir dans ce post.</p> <h2 class="heading-element" id="création-du-plugin"><span>Création du plugin</span> <a href="#cr%c3%a9ation-du-plugin" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Backstage fournis un provider OIDC mais pas de plugin prêt à l&rsquo;emploi. Nous allons devoir créer un module pour étendre le plugin Auth de backstage avec notre support de OIDC. Nous utilisons la CLI de backstage pour cela</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a> </span><span class="lnt" id="hl-0-3"><a class="lnlinks" href="#hl-0-3">3</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">backstage-cli new --select backend-module </span></span><span class="line"><span class="cl"><span class="c1"># ? Enter the ID of the plugin [required] : auth</span> </span></span><span class="line"><span class="cl"><span class="c1"># ? Enter the ID of the module [required] : oidc</span></span></span></code></pre></td></tr></table> </div> </div><p>Backstage à maintenant générer une structure de plugin dans le dossier <code>plugins/auth-backend-module-oidc</code>. Commençons par ajouter la dépendences au provider OIDC fournis par Backstage</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a> </span><span class="lnt" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">cd</span> plugins/auth-backend-module-oidc </span></span><span class="line"><span class="cl">yarn add @backstage/plugin-auth-backend-module-oidc-provider</span></span></code></pre></td></tr></table> </div> </div><p>Une fois cela fait, dans le fichier <code>src/module.ts</code>, remplacer le contenu par le suivant:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a> </span><span class="lnt" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a> </span><span class="lnt" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a> </span><span class="lnt" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a> </span><span class="lnt" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a> </span><span class="lnt" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a> </span><span class="lnt" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a> </span><span class="lnt" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a> </span><span class="lnt" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a> </span><span class="lnt" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a> </span><span class="lnt" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a> </span><span class="lnt" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a> </span><span class="lnt" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a> </span><span class="lnt" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a> </span><span class="lnt" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a> </span><span class="lnt" id="hl-2-16"><a class="lnlinks" href="#hl-2-16">16</a> </span><span class="lnt" id="hl-2-17"><a class="lnlinks" href="#hl-2-17">17</a> </span><span class="lnt" id="hl-2-18"><a class="lnlinks" href="#hl-2-18">18</a> </span><span class="lnt" id="hl-2-19"><a class="lnlinks" href="#hl-2-19">19</a> </span><span class="lnt" id="hl-2-20"><a class="lnlinks" href="#hl-2-20">20</a> </span><span class="lnt" id="hl-2-21"><a class="lnlinks" href="#hl-2-21">21</a> </span><span class="lnt" id="hl-2-22"><a class="lnlinks" href="#hl-2-22">22</a> </span><span class="lnt" id="hl-2-23"><a class="lnlinks" href="#hl-2-23">23</a> </span><span class="lnt" id="hl-2-24"><a class="lnlinks" href="#hl-2-24">24</a> </span><span class="lnt" id="hl-2-25"><a class="lnlinks" href="#hl-2-25">25</a> </span><span class="lnt" id="hl-2-26"><a class="lnlinks" href="#hl-2-26">26</a> </span><span class="lnt" id="hl-2-27"><a class="lnlinks" href="#hl-2-27">27</a> </span><span class="lnt" id="hl-2-28"><a class="lnlinks" href="#hl-2-28">28</a> </span><span class="lnt" id="hl-2-29"><a class="lnlinks" href="#hl-2-29">29</a> </span><span class="lnt" id="hl-2-30"><a class="lnlinks" href="#hl-2-30">30</a> </span><span class="lnt" id="hl-2-31"><a class="lnlinks" href="#hl-2-31">31</a> </span><span class="lnt" id="hl-2-32"><a class="lnlinks" href="#hl-2-32">32</a> </span><span class="lnt" id="hl-2-33"><a class="lnlinks" href="#hl-2-33">33</a> </span><span class="lnt" id="hl-2-34"><a class="lnlinks" href="#hl-2-34">34</a> </span><span class="lnt" id="hl-2-35"><a class="lnlinks" href="#hl-2-35">35</a> </span><span class="lnt" id="hl-2-36"><a class="lnlinks" href="#hl-2-36">36</a> </span><span class="lnt" id="hl-2-37"><a class="lnlinks" href="#hl-2-37">37</a> </span><span class="lnt" id="hl-2-38"><a class="lnlinks" href="#hl-2-38">38</a> </span><span class="lnt" id="hl-2-39"><a class="lnlinks" href="#hl-2-39">39</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">oidcAuthenticator</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@backstage/plugin-auth-backend-module-oidc-provider&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">authProvidersExtensionPoint</span><span class="p">,</span> <span class="nx">createOAuthProviderFactory</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@backstage/plugin-auth-node&#39;</span> </span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">coreServices</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">createBackendModule</span><span class="p">,</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@backstage/backend-plugin-api&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">DEFAULT_NAMESPACE</span><span class="p">,</span> <span class="nx">stringifyEntityRef</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@backstage/catalog-model&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">authModuleOidc</span> <span class="o">=</span> <span class="nx">createBackendModule</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="nx">pluginId</span><span class="o">:</span> <span class="s1">&#39;auth&#39;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">moduleId</span><span class="o">:</span> <span class="s1">&#39;oidc&#39;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">register</span><span class="p">(</span><span class="nx">reg</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">reg</span><span class="p">.</span><span class="nx">registerInit</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="nx">deps</span><span class="o">:</span> <span class="p">{</span> <span class="nx">logger</span>: <span class="kt">coreServices.logger</span><span class="p">,</span> <span class="nx">providers</span>: <span class="kt">authProvidersExtensionPoint</span> <span class="p">},</span> </span></span><span class="line"><span class="cl"> <span class="kr">async</span> <span class="nx">init</span><span class="p">({</span> <span class="nx">logger</span><span class="p">,</span> <span class="nx">providers</span> <span class="p">})</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">logger</span><span class="p">.</span><span class="nx">info</span><span class="p">(</span><span class="s1">&#39;Registering OIDC provider!&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nx">providers</span><span class="p">.</span><span class="nx">registerProvider</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="nx">providerId</span><span class="o">:</span> <span class="s1">&#39;sso-auth-provider&#39;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">factory</span>: <span class="kt">createOAuthProviderFactory</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="nx">authenticator</span>: <span class="kt">oidcAuthenticator</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="kr">async</span> <span class="nx">signInResolver</span><span class="p">(</span><span class="nx">info</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">userRef</span> <span class="o">=</span> <span class="nx">stringifyEntityRef</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="nx">kind</span><span class="o">:</span> <span class="s1">&#39;User&#39;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">name</span>: <span class="kt">info.result.fullProfile.userinfo.sub</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="kr">namespace</span><span class="o">:</span> <span class="nx">DEFAULT_NAMESPACE</span> </span></span><span class="line"><span class="cl"> <span class="p">});</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">issueToken</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="nx">claims</span><span class="o">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">sub</span>: <span class="kt">userRef</span><span class="p">,</span> <span class="c1">// The user&#39;s own identity </span></span></span><span class="line"><span class="cl"> <span class="nx">ent</span><span class="o">:</span> <span class="p">[</span><span class="nx">userRef</span><span class="p">],</span> <span class="c1">// A list of identities that the user claims ownership through </span></span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">});</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">});</span> </span></span><span class="line"><span class="cl"> <span class="p">},</span> </span></span><span class="line"><span class="cl"> <span class="p">});</span> </span></span><span class="line"><span class="cl"> <span class="p">},</span> </span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></td></tr></table> </div> </div><p>Notre plugin est maintenant opérationel. Vérifions que le module est bien activé dans <code>packages/backend/index.ts</code>. La ligne suivant doit être présente :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a class="lnlinks" href="#hl-3-1">1</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="nx">backend</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="kr">import</span><span class="p">(</span><span class="s1">&#39;backstage-plugin-auth-backend-module-oidc&#39;</span><span class="p">))</span></span></span></code></pre></td></tr></table> </div> </div><h2 class="heading-element" id="configuration-de-lapi-factory"><span>Configuration de l&rsquo;Api Factory</span> <a href="#configuration-de-lapi-factory" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Nous allons maintenant référencer le plugin comme API d&rsquo;autentification dans le front. Pour cela ajouter le code suivant dans le fichier <code>packages/app/src/apis.ts</code> :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a class="lnlinks" href="#hl-4-1"> 1</a> </span><span class="lnt" id="hl-4-2"><a class="lnlinks" href="#hl-4-2"> 2</a> </span><span class="lnt" id="hl-4-3"><a class="lnlinks" href="#hl-4-3"> 3</a> </span><span class="lnt" id="hl-4-4"><a class="lnlinks" href="#hl-4-4"> 4</a> </span><span class="lnt" id="hl-4-5"><a class="lnlinks" href="#hl-4-5"> 5</a> </span><span class="lnt" id="hl-4-6"><a class="lnlinks" href="#hl-4-6"> 6</a> </span><span class="lnt" id="hl-4-7"><a class="lnlinks" href="#hl-4-7"> 7</a> </span><span class="lnt" id="hl-4-8"><a class="lnlinks" href="#hl-4-8"> 8</a> </span><span class="lnt" id="hl-4-9"><a class="lnlinks" href="#hl-4-9"> 9</a> </span><span class="lnt" id="hl-4-10"><a class="lnlinks" href="#hl-4-10">10</a> </span><span class="lnt" id="hl-4-11"><a class="lnlinks" href="#hl-4-11">11</a> </span><span class="lnt" id="hl-4-12"><a class="lnlinks" href="#hl-4-12">12</a> </span><span class="lnt" id="hl-4-13"><a class="lnlinks" href="#hl-4-13">13</a> </span><span class="lnt" id="hl-4-14"><a class="lnlinks" href="#hl-4-14">14</a> </span><span class="lnt" id="hl-4-15"><a class="lnlinks" href="#hl-4-15">15</a> </span><span class="lnt" id="hl-4-16"><a class="lnlinks" href="#hl-4-16">16</a> </span><span class="lnt" id="hl-4-17"><a class="lnlinks" href="#hl-4-17">17</a> </span><span class="lnt" id="hl-4-18"><a class="lnlinks" href="#hl-4-18">18</a> </span><span class="lnt" id="hl-4-19"><a class="lnlinks" href="#hl-4-19">19</a> </span><span class="lnt" id="hl-4-20"><a class="lnlinks" href="#hl-4-20">20</a> </span><span class="lnt" id="hl-4-21"><a class="lnlinks" href="#hl-4-21">21</a> </span><span class="lnt" id="hl-4-22"><a class="lnlinks" href="#hl-4-22">22</a> </span><span class="lnt" id="hl-4-23"><a class="lnlinks" href="#hl-4-23">23</a> </span><span class="lnt" id="hl-4-24"><a class="lnlinks" href="#hl-4-24">24</a> </span><span class="lnt" id="hl-4-25"><a class="lnlinks" href="#hl-4-25">25</a> </span><span class="lnt" id="hl-4-26"><a class="lnlinks" href="#hl-4-26">26</a> </span><span class="lnt" id="hl-4-27"><a class="lnlinks" href="#hl-4-27">27</a> </span><span class="lnt" id="hl-4-28"><a class="lnlinks" href="#hl-4-28">28</a> </span><span class="lnt" id="hl-4-29"><a class="lnlinks" href="#hl-4-29">29</a> </span><span class="lnt" id="hl-4-30"><a class="lnlinks" href="#hl-4-30">30</a> </span><span class="lnt" id="hl-4-31"><a class="lnlinks" href="#hl-4-31">31</a> </span><span class="lnt" id="hl-4-32"><a class="lnlinks" href="#hl-4-32">32</a> </span><span class="lnt" id="hl-4-33"><a class="lnlinks" href="#hl-4-33">33</a> </span><span class="lnt" id="hl-4-34"><a class="lnlinks" href="#hl-4-34">34</a> </span><span class="lnt" id="hl-4-35"><a class="lnlinks" href="#hl-4-35">35</a> </span><span class="lnt" id="hl-4-36"><a class="lnlinks" href="#hl-4-36">36</a> </span><span class="lnt" id="hl-4-37"><a class="lnlinks" href="#hl-4-37">37</a> </span><span class="lnt" id="hl-4-38"><a class="lnlinks" href="#hl-4-38">38</a> </span><span class="lnt" id="hl-4-39"><a class="lnlinks" href="#hl-4-39">39</a> </span><span class="lnt" id="hl-4-40"><a class="lnlinks" href="#hl-4-40">40</a> </span><span class="lnt" id="hl-4-41"><a class="lnlinks" href="#hl-4-41">41</a> </span><span class="lnt" id="hl-4-42"><a class="lnlinks" href="#hl-4-42">42</a> </span><span class="lnt" id="hl-4-43"><a class="lnlinks" href="#hl-4-43">43</a> </span><span class="lnt" id="hl-4-44"><a class="lnlinks" href="#hl-4-44">44</a> </span><span class="lnt" id="hl-4-45"><a class="lnlinks" href="#hl-4-45">45</a> </span><span class="lnt" id="hl-4-46"><a class="lnlinks" href="#hl-4-46">46</a> </span><span class="lnt" id="hl-4-47"><a class="lnlinks" href="#hl-4-47">47</a> </span><span class="lnt" id="hl-4-48"><a class="lnlinks" href="#hl-4-48">48</a> </span><span class="lnt" id="hl-4-49"><a class="lnlinks" href="#hl-4-49">49</a> </span><span class="lnt" id="hl-4-50"><a class="lnlinks" href="#hl-4-50">50</a> </span><span class="lnt" id="hl-4-51"><a class="lnlinks" href="#hl-4-51">51</a> </span><span class="lnt" id="hl-4-52"><a class="lnlinks" href="#hl-4-52">52</a> </span><span class="lnt" id="hl-4-53"><a class="lnlinks" href="#hl-4-53">53</a> </span><span class="lnt" id="hl-4-54"><a class="lnlinks" href="#hl-4-54">54</a> </span><span class="lnt" id="hl-4-55"><a class="lnlinks" href="#hl-4-55">55</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">AnyApiFactory</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">ApiRef</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">BackstageIdentityApi</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">configApiRef</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">createApiFactory</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">createApiRef</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">discoveryApiRef</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">oauthRequestApiRef</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">OpenIdConnectApi</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">ProfileInfoApi</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">SessionApi</span><span class="p">,</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@backstage/core-plugin-api&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">OAuth2</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@backstage/core-app-api&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">oidcAuthApiRef</span>: <span class="kt">ApiRef</span> <span class="o">&lt;</span> </span></span><span class="line"><span class="cl"> <span class="nx">OpenIdConnectApi</span> <span class="o">&amp;</span> <span class="c1">// The OIDC API that will handle authentification </span></span></span><span class="line"><span class="cl"> <span class="nx">ProfileInfoApi</span> <span class="o">&amp;</span> <span class="c1">// Profile API for requesting user profile info from the auth provider in question </span></span></span><span class="line"><span class="cl"> <span class="nx">BackstageIdentityApi</span> <span class="o">&amp;</span> <span class="c1">// Backstage Identity API to handle and associate the user profile with backstage identity </span></span></span><span class="line"><span class="cl"> <span class="nx">SessionApi</span> <span class="c1">// Sesssion API, to handle the session the user will have while logged in </span></span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="o">=</span> <span class="nx">createApiRef</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;sso-auth-provider&#39;</span> <span class="c1">// Can be anything as long as it doesn&#39;t conflict with other API ref IDs </span></span></span><span class="line"><span class="cl"><span class="p">})</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">apis</span>: <span class="kt">AnyApiFactory</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="nx">createApiFactory</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="nx">api</span>: <span class="kt">oidcAuthApiRef</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">deps</span><span class="o">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">discoveryApi</span>: <span class="kt">discoveryApiRef</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">oauthRequestApi</span>: <span class="kt">oauthRequestApiRef</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">configApi</span>: <span class="kt">configApiRef</span> </span></span><span class="line"><span class="cl"> <span class="p">},</span> </span></span><span class="line"><span class="cl"> <span class="nx">factory</span><span class="o">:</span> <span class="p">({</span> <span class="nx">discoveryApi</span><span class="p">,</span> <span class="nx">oauthRequestApi</span><span class="p">,</span> <span class="nx">configApi</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="nx">Oauth2</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="nx">configApi</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">discoveryApi</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">oauthRequestApi</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">provider</span><span class="o">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;sso-auth-provider&#39;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;Enterprise SSO&#39;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">icon</span><span class="o">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kc">null</span> </span></span><span class="line"><span class="cl"> <span class="p">},</span> </span></span><span class="line"><span class="cl"> <span class="nx">environment</span>: <span class="kt">configApi.getOptionalString</span><span class="p">(</span><span class="s1">&#39;auth.environment&#39;</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="nx">defaultScopes</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;openId&#39;</span><span class="p">,</span> <span class="s1">&#39;profile&#39;</span><span class="p">,</span> <span class="s1">&#39;email&#39;</span><span class="p">],</span> </span></span><span class="line"><span class="cl"> <span class="nx">popupOptions</span><span class="o">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">size</span><span class="o">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// fullscreen: true </span></span></span><span class="line"><span class="cl"> <span class="c1">// or specify popup width and height </span></span></span><span class="line"><span class="cl"> <span class="nx">width</span>: <span class="kt">1000</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">height</span>: <span class="kt">1000</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">}),</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Other createApiFactory </span></span></span><span class="line"><span class="cl"><span class="p">]</span></span></span></code></pre></td></tr></table> </div> </div><h2 class="heading-element" id="ajout-du-bouton-de-connexion-dans-linterface"><span>Ajout du bouton de connexion dans l&rsquo;interface</span> <a href="#ajout-du-bouton-de-connexion-dans-linterface" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Allons maintenant dans le fichier <code>packages/app/src/App.tsx</code> pour ajouter le bouton de connexion à backstage.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a class="lnlinks" href="#hl-5-1"> 1</a> </span><span class="lnt" id="hl-5-2"><a class="lnlinks" href="#hl-5-2"> 2</a> </span><span class="lnt" id="hl-5-3"><a class="lnlinks" href="#hl-5-3"> 3</a> </span><span class="lnt" id="hl-5-4"><a class="lnlinks" href="#hl-5-4"> 4</a> </span><span class="lnt" id="hl-5-5"><a class="lnlinks" href="#hl-5-5"> 5</a> </span><span class="lnt" id="hl-5-6"><a class="lnlinks" href="#hl-5-6"> 6</a> </span><span class="lnt" id="hl-5-7"><a class="lnlinks" href="#hl-5-7"> 7</a> </span><span class="lnt" id="hl-5-8"><a class="lnlinks" href="#hl-5-8"> 8</a> </span><span class="lnt" id="hl-5-9"><a class="lnlinks" href="#hl-5-9"> 9</a> </span><span class="lnt" id="hl-5-10"><a class="lnlinks" href="#hl-5-10">10</a> </span><span class="lnt" id="hl-5-11"><a class="lnlinks" href="#hl-5-11">11</a> </span><span class="lnt" id="hl-5-12"><a class="lnlinks" href="#hl-5-12">12</a> </span><span class="lnt" id="hl-5-13"><a class="lnlinks" href="#hl-5-13">13</a> </span><span class="lnt" id="hl-5-14"><a class="lnlinks" href="#hl-5-14">14</a> </span><span class="lnt" id="hl-5-15"><a class="lnlinks" href="#hl-5-15">15</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">apis</span><span class="p">,</span> <span class="nx">oidcAuthApiRef</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;./apis&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">configApiRef</span><span class="p">,</span> <span class="nx">useApi</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@backstage/core-plugin-api&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">createApp</span><span class="p">({</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Other stuff... </span></span></span><span class="line"><span class="cl"> <span class="nx">components</span><span class="o">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">SignInPage</span>: <span class="kt">props</span> <span class="o">=&gt;</span> <span class="p">(&lt;</span><span class="nt">SignInPage</span> <span class="p">{</span><span class="na">...props</span><span class="p">}</span> <span class="na">auto</span> <span class="na">providers</span><span class="o">=</span><span class="p">{</span> <span class="nx">useApi</span><span class="p">(</span><span class="nx">configApiRef</span><span class="p">).</span><span class="nx">getString</span><span class="p">(</span><span class="s1">&#39;auth.environment&#39;</span><span class="p">)</span> <span class="o">===</span> <span class="s1">&#39;development&#39;</span> <span class="o">?</span> <span class="p">[</span><span class="s1">&#39;guest&#39;</span><span class="p">]</span> <span class="o">:</span> <span class="p">[{</span> </span></span><span class="line"><span class="cl"> <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;sso-auth-provider&#39;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;Enterprise SSO&#39;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s1">&#39;Sign in with Enterprise SSO&#39;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nx">apiRef</span>: <span class="kt">oidcAuthApiRef</span> </span></span><span class="line"><span class="cl"> <span class="p">}]}/&gt;)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Other stuff... </span></span></span><span class="line"><span class="cl"><span class="p">})</span></span></span></code></pre></td></tr></table> </div> </div><p>Ajoutez la configuration suivante dans votre fichier <code>app-config.local.yaml</code>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a class="lnlinks" href="#hl-6-1">1</a> </span><span class="lnt" id="hl-6-2"><a class="lnlinks" href="#hl-6-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">auth</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">development</span></span></span></code></pre></td></tr></table> </div> </div><p>et dans le fichier <code>app-config.production.yaml</code></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-7-1"><a class="lnlinks" href="#hl-7-1">1</a> </span><span class="lnt" id="hl-7-2"><a class="lnlinks" href="#hl-7-2">2</a> </span><span class="lnt" id="hl-7-3"><a class="lnlinks" href="#hl-7-3">3</a> </span><span class="lnt" id="hl-7-4"><a class="lnlinks" href="#hl-7-4">4</a> </span><span class="lnt" id="hl-7-5"><a class="lnlinks" href="#hl-7-5">5</a> </span><span class="lnt" id="hl-7-6"><a class="lnlinks" href="#hl-7-6">6</a> </span><span class="lnt" id="hl-7-7"><a class="lnlinks" href="#hl-7-7">7</a> </span><span class="lnt" id="hl-7-8"><a class="lnlinks" href="#hl-7-8">8</a> </span><span class="lnt" id="hl-7-9"><a class="lnlinks" href="#hl-7-9">9</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">auth</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">production</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">providers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">guest</span><span class="p">:</span><span class="w"> </span>{}<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">sso-auth-provider</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">production</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">metadataUrl</span><span class="p">:</span><span class="w"> </span><span class="l">${AUTH_REALM_URL}/.well-known/openid-configuration</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">clientId</span><span class="p">:</span><span class="w"> </span><span class="l">${AUTH_MY_CLIENT_ID}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">clientSecret</span><span class="p">:</span><span class="w"> </span><span class="l">${AUTH_MY_CLIENT_SECRET}</span></span></span></code></pre></td></tr></table> </div> </div><p>Avec la configuration ci-dessus, l&rsquo;authentification se fera comme guest en mode developement et via SSO en mode production. Pour plus d&rsquo;information, vous pouvez consulter la <a href="https://backstage.io/docs/auth/oidc/"target="_blank" rel="external nofollow noopener noreferrer">documentation officielle</a></p> <p>Félicitation votre application backstage supporte maintenant la connexion via OIDC. Jetez également un œil à l&rsquo;<a href="https://janus-idp.io/plugins/keycloak/"target="_blank" rel="external nofollow noopener noreferrer">intégration Keycloak</a> pour l&rsquo;import automatique des utilisateurs et des groupes dans Backstage.</p> Quarkus Extension Synthetic Build Item https://techland.info/posts/quarkus/quarkus-extension-synthetic-build-item/ Sun, 07 Jul 2024 12:29:05 +0200 https://techland.info/posts/quarkus/quarkus-extension-synthetic-build-item/ Développement <p>Dans le précédent post sur le buildItem <a href="https://techland.info/posts/quarkus/quarkus-extension-additional-bean-build-item">AdditionalBeanBuildItem</a>, nous avons vu comment référencer un bean au gestionnaire de dépendance ARC. Cela est très pratique, mais dans certains cas, nous voulons pouvoir que le gestionnaire injecte une instance spécifique d&rsquo;un bean déjà configuré. L&rsquo;<code>AdditionalBeanBuildItem</code> ne prenant qu&rsquo;une classe de bean en paramètre, il ne peut pas être utilisé pour cela. C&rsquo;est ici qu&rsquo;intervient le <code>SyntheticBeanBuildItem</code></p> <h2 class="heading-element" id="cas-dusage"><span>Cas d&rsquo;usage.</span> <a href="#cas-dusage" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Imaginons que votre extension souhaite fournir une configuration sous forme d&rsquo;un bean injectable pour aller communiquer avec une API tierce. Il va falloir dire à Quarkus quelle instance de bean injecter et avec quelles valeurs.</p> <h2 class="heading-element" id="syntheticbeanbuilditem"><span>SyntheticBeanBuildItem</span> <a href="#syntheticbeanbuilditem" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Le nom de ce buildItem n&rsquo;est pas très parlant et j&rsquo;ai mis pas mal de temps à comprendre à quoi il pouvait bien servir. En résumé, il s&rsquo;agit d&rsquo;un <code>AdditionalBeanBuildItem</code> prenant une instance de classe au lieu d&rsquo;une classe en paramètre. Cela le rend très puissant. Pour produire une instance de classe, il va tout d&rsquo;abord falloir fournir cette instance sous la forme d&rsquo;un <code>Recorder</code>.</p> <h3 class="heading-element" id="recorder"><span>Recorder</span> <a href="#recorder" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>Commençons par créer notre Bean de configuration d&rsquo;API dans le module <code>runtime</code> de notre extension :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a> </span><span class="lnt" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a> </span><span class="lnt" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a> </span><span class="lnt" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a> </span><span class="lnt" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a> </span><span class="lnt" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a> </span><span class="lnt" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a> </span><span class="lnt" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a> </span><span class="lnt" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a> </span><span class="lnt" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a> </span><span class="lnt" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a> </span><span class="lnt" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a> </span><span class="lnt" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a> </span><span class="lnt" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a> </span><span class="lnt" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a> </span><span class="lnt" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a> </span><span class="lnt" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a> </span><span class="lnt" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a> </span><span class="lnt" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a> </span><span class="lnt" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@ApplicationScoped</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Config</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Only for CDI injection during mvn package phase</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@SuppressWarnings</span><span class="p">(</span><span class="s">&#34;unused&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="nf">Config</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">Config</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getUrl</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setUrl</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">url</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Notre bean doit être &ldquo;CDI compatible&rdquo;. Pour cela, il doit comporter un constructeur par défaut ainsi que des accesseurs. Dans notre cas, nous utilisons un constructeur par défaut privé pour que la classe ne puisse pas être utilisée d&rsquo;une manière non prévue. Le plugin maven quarkus vérifie la présence des Bean injectable lors de la phase de package. Sans ce constructeur par défaut le build de notre projet plante avec une erreur <code>Unsatisfied dependency</code>. Nous définissons notre bean en scope <code>@ApplicationScoped</code> pour que la même instance soit toujours injectée.</p> <p>Créons maintenant le recorder pour notre classe Config :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a> </span><span class="lnt" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a> </span><span class="lnt" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a> </span><span class="lnt" id="hl-1-4"><a class="lnlinks" href="#hl-1-4">4</a> </span><span class="lnt" id="hl-1-5"><a class="lnlinks" href="#hl-1-5">5</a> </span><span class="lnt" id="hl-1-6"><a class="lnlinks" href="#hl-1-6">6</a> </span><span class="lnt" id="hl-1-7"><a class="lnlinks" href="#hl-1-7">7</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Recorder</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">ConfigRecorder</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">RuntimeValue</span><span class="o">&lt;</span><span class="n">Config</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">createConfig</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">url</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RuntimeValue</span><span class="o">&lt;&gt;</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">Config</span><span class="p">(</span><span class="n">url</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Lorsque qu&rsquo;un recorder est déclaré, Quarkus va exécuter le recorder lors de la phase de build et enregistrer le bytecode produit par ce recorder. Lors de la phase de runtime, quarkus fournira se bytecode directement au lieu d&rsquo;avoir à le recalculer dynamiquement. Cela permet d&rsquo;accélérer grandement le temps de démarrage. Dans notre exemple, le recorder retourne un object <code>RuntimeValue&lt;Config&gt;</code> contenant l&rsquo;instance de notre configuration. Ce <code>RuntimeValue</code> sera consommer par notre SyntheticBeanBuildItem.</p> <h3 class="heading-element" id="buildstep"><span>BuildStep</span> <a href="#buildstep" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a> </span><span class="lnt" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a> </span><span class="lnt" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a> </span><span class="lnt" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a> </span><span class="lnt" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a> </span><span class="lnt" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a> </span><span class="lnt" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a> </span><span class="lnt" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a> </span><span class="lnt" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a> </span><span class="lnt" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a> </span><span class="lnt" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a> </span><span class="lnt" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a> </span><span class="lnt" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a> </span><span class="lnt" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a> </span><span class="lnt" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@Record</span><span class="p">(</span><span class="n">ExecutionTime</span><span class="p">.</span><span class="na">RUNTIME_INIT</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BuildStep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">createConfig</span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">DevServicesResultBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">devServicesResultBuildItem</span><span class="p">,</span><span class="w"> </span><span class="n">ConfigRecorder</span><span class="w"> </span><span class="n">recorder</span><span class="p">,</span><span class="w"> </span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">SyntheticBeanBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">syntheticBeanBuildItemBuildProducer</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Retrieve config set Inside ApiContainer</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">apiUrl</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">devServicesResultBuildItem</span><span class="p">.</span><span class="na">stream</span><span class="p">().</span><span class="na">filter</span><span class="p">(</span><span class="n">devService</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">devService</span><span class="p">.</span><span class="na">getName</span><span class="p">().</span><span class="na">equals</span><span class="p">(</span><span class="n">ApiContainer</span><span class="p">.</span><span class="na">CONTAINER_NAME</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">findFirst</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">orElseThrow</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalStateException</span><span class="p">(</span><span class="s">&#34;Can&#39;t find api url&#34;</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">getConfig</span><span class="p">().</span><span class="na">get</span><span class="p">(</span><span class="s">&#34;url&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">syntheticBeanBuildItemBuildProducer</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="n">SyntheticBeanBuildItem</span><span class="p">.</span><span class="na">configure</span><span class="p">(</span><span class="n">ConfigRecorder</span><span class="p">.</span><span class="na">class</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">unremovable</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">setRuntimeInit</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">runtimeValue</span><span class="p">(</span><span class="n">recorder</span><span class="p">.</span><span class="na">createConfig</span><span class="p">(</span><span class="s">&#34;http://&#34;</span><span class="o">+</span><span class="n">apiUrl</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">done</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Pour configurer notre syntheticBuildItem nous utilisons une méthode annotée avec <code>@Record(ExecutionTime.RUNTIME_INIT)</code>. Ici, le <code>ExecutionTime.STATIC_INIT</code> ne fonctionnera pas, car notre recorder ne sera pas encore disponible. Ensuite, nous récupérons notre URL d&rsquo;API via un <code>DevServicesResultBuildItem</code> dont nous parlerons dans un prochain article. Une fois l&rsquo;url de l&rsquo;API récupérée, nous construisons notre <code>SyntheticBeanBuildItem</code> avec notre recorder. La méthode <code>unremovable</code> s&rsquo;assure que notre instance de bean ne sera pas supprimer par Quarkus lors de la compilation de notre extension même si ce bean n&rsquo;est pas utilisé. Sans cela, notre bean ne sera pas accessible dans le module maven important notre extension. Comme nous avons utilisé <code>@Record(ExecutionTime.RUNTIME_INIT)</code>, nous informons Quarkus que notre instance de bean sera disponible lors de la phase <code>RuntimeInit</code> grâce à la méthode <code>setRuntimeInit</code>. Enfin, nous passons l&rsquo;instance de notre bean de config à la méthode <code>runtimeValue</code> pour finir d&rsquo;initialiser notre <code>SyntheticBeanBuildItem</code>.</p> <p>Si vous n&rsquo;avez pas le contrôle sur la classe du bean produit, vous pouvez ajouter un qualifier manquant via la méthode <code>.addQualifier(ApplicationScoped.class)</code></p> <h2 class="heading-element" id="injection-du-bean"><span>Injection du Bean</span> <a href="#injection-du-bean" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Dans un projet important notre extension :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a class="lnlinks" href="#hl-3-1">1</a> </span><span class="lnt" id="hl-3-2"><a class="lnlinks" href="#hl-3-2">2</a> </span><span class="lnt" id="hl-3-3"><a class="lnlinks" href="#hl-3-3">3</a> </span><span class="lnt" id="hl-3-4"><a class="lnlinks" href="#hl-3-4">4</a> </span><span class="lnt" id="hl-3-5"><a class="lnlinks" href="#hl-3-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;dependency&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;groupId&gt;</span>org.example<span class="nt">&lt;/groupId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;artifactId&gt;</span>example-extension<span class="nt">&lt;/artifactId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;version&gt;</span>1.0-SNAPSHOT<span class="nt">&lt;/version&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/dependency&gt;</span></span></span></code></pre></td></tr></table> </div> </div><p>Nous pouvons maintenant injecter notre Bean de configuration pour l&rsquo;utiliser :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a class="lnlinks" href="#hl-4-1">1</a> </span><span class="lnt" id="hl-4-2"><a class="lnlinks" href="#hl-4-2">2</a> </span><span class="lnt" id="hl-4-3"><a class="lnlinks" href="#hl-4-3">3</a> </span><span class="lnt" id="hl-4-4"><a class="lnlinks" href="#hl-4-4">4</a> </span><span class="lnt" id="hl-4-5"><a class="lnlinks" href="#hl-4-5">5</a> </span><span class="lnt" id="hl-4-6"><a class="lnlinks" href="#hl-4-6">6</a> </span><span class="lnt" id="hl-4-7"><a class="lnlinks" href="#hl-4-7">7</a> </span><span class="lnt" id="hl-4-8"><a class="lnlinks" href="#hl-4-8">8</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">MyClassExample</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Config</span><span class="w"> </span><span class="n">config</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">MyClassExample</span><span class="p">(</span><span class="n">Config</span><span class="w"> </span><span class="n">config</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">config</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">config</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>## Conclusion</p> <p>Un <code>SyntheticBeanBuildItem</code> est le pendant dynamique de l&rsquo;<code>AdditionalBeanBuildItem</code>. Il utilise un <code>Recorder</code> pour enregistrer le bytecode produit par notre instance lors de la phase de Build de quarkus. Ce bytecode sera fournis tel quel lors de l&rsquo;exécution de l&rsquo;application afin d&rsquo;accélérer son démarrage.</p> Test JUnit avec un mock de Keycloak https://techland.info/posts/security/junit-test-with-mocked-keycloak/ Wed, 29 May 2024 07:55:31 +0200 https://techland.info/posts/security/junit-test-with-mocked-keycloak/ Sécurité <p>J&rsquo;ai récemment dû implémenter une bibliothèque permettant de valider un JWT dans le cadre d&rsquo;une authentification OIDC avec Keycloak. Afin de tester que ma bibliothèque fonctionnait correctement au niveau de la partie validation du token, j&rsquo;ai dû générer un token signé. Ma bibliothèque utilise la bibliothèque de Keycloak pour la vérification. Celle de keycloak fait appel au well-known pour récupérer les informations sur la clé RSA qui a servi à signer les tokens. Voyons un peu comment faire tout cela sans avoir à démarrer et à configurer un Keycloak entier juste pour des tests.</p> <h2 class="heading-element" id="mock-de-keycloak"><span>Mock de keycloak</span> <a href="#mock-de-keycloak" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Pour commencer, nous allons devoir mock l&rsquo;appel au endpoint well-known de Keycloak réalisé par la bibliothèque. Pour cela, nous allons utiliser <code>MockServer</code>. Commençons par ajouter la dépendance à notre <code>pom.xml</code>.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a> </span><span class="lnt" id="hl-0-3"><a class="lnlinks" href="#hl-0-3">3</a> </span><span class="lnt" id="hl-0-4"><a class="lnlinks" href="#hl-0-4">4</a> </span><span class="lnt" id="hl-0-5"><a class="lnlinks" href="#hl-0-5">5</a> </span><span class="lnt" id="hl-0-6"><a class="lnlinks" href="#hl-0-6">6</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;dependency&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;groupId&gt;</span>org.mock-server<span class="nt">&lt;/groupId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;artifactID&gt;</span>mockserver-netty<span class="nt">&lt;/artifactId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;version&gt;</span>5.15.0<span class="nt">&lt;/version&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/dependency&gt;</span></span></span></code></pre></td></tr></table> </div> </div><p>Une fois, ceci fait, on va configurer le mock server pour exposer un endpoint well-known à la place de keycloak.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a> </span><span class="lnt" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a> </span><span class="lnt" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a> </span><span class="lnt" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a> </span><span class="lnt" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a> </span><span class="lnt" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a> </span><span class="lnt" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a> </span><span class="lnt" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a> </span><span class="lnt" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a> </span><span class="lnt" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a> </span><span class="lnt" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a> </span><span class="lnt" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a> </span><span class="lnt" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a> </span><span class="lnt" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a> </span><span class="lnt" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a> </span><span class="lnt" id="hl-1-16"><a class="lnlinks" href="#hl-1-16">16</a> </span><span class="lnt" id="hl-1-17"><a class="lnlinks" href="#hl-1-17">17</a> </span><span class="lnt" id="hl-1-18"><a class="lnlinks" href="#hl-1-18">18</a> </span><span class="lnt" id="hl-1-19"><a class="lnlinks" href="#hl-1-19">19</a> </span><span class="lnt" id="hl-1-20"><a class="lnlinks" href="#hl-1-20">20</a> </span><span class="lnt" id="hl-1-21"><a class="lnlinks" href="#hl-1-21">21</a> </span><span class="lnt" id="hl-1-22"><a class="lnlinks" href="#hl-1-22">22</a> </span><span class="lnt" id="hl-1-23"><a class="lnlinks" href="#hl-1-23">23</a> </span><span class="lnt" id="hl-1-24"><a class="lnlinks" href="#hl-1-24">24</a> </span><span class="lnt" id="hl-1-25"><a class="lnlinks" href="#hl-1-25">25</a> </span><span class="lnt" id="hl-1-26"><a class="lnlinks" href="#hl-1-26">26</a> </span><span class="lnt" id="hl-1-27"><a class="lnlinks" href="#hl-1-27">27</a> </span><span class="lnt" id="hl-1-28"><a class="lnlinks" href="#hl-1-28">28</a> </span><span class="lnt" id="hl-1-29"><a class="lnlinks" href="#hl-1-29">29</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">Class</span><span class="w"> </span><span class="n">OIDCTest</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">ClientAndServer</span><span class="w"> </span><span class="n">mockServer</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">KEYCLOAK_PORT</span><span class="o">=</span><span class="n">8080</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@BeforeAll</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">beforeAll</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">mockServer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ClientAndServer</span><span class="p">.</span><span class="na">startClientAndServer</span><span class="p">(</span><span class="n">KEYCLOAK_PORT</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">MockServerClient</span><span class="w"> </span><span class="n">mockServerClient</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">MockServerClient</span><span class="p">(</span><span class="s">&#34;localhost&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">KEYCLOAK_PORT</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">mockServerClient</span><span class="p">.</span><span class="na">when</span><span class="p">(</span><span class="n">HttpRequest</span><span class="p">.</span><span class="na">request</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">withMethod</span><span class="p">(</span><span class="s">&#34;GET&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">withPath</span><span class="p">(</span><span class="s">&#34;/realms/exemple/.well-known/openid-configuration&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">withHeader</span><span class="p">(</span><span class="s">&#34;accept&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;application/json&#34;</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">respond</span><span class="p">(</span><span class="n">HttpResponse</span><span class="p">.</span><span class="na">response</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">withStatusCode</span><span class="p">(</span><span class="n">200</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">withBody</span><span class="p">(</span><span class="s">&#34;{ \&#34;jwks_uri\&#34;: \&#34;http://localhost:8080/realms/example/protocol/openid-connect/certs\&#34;,&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;\&#34;authorization_endpoint\&#34;: \&#34;http://localhost:8080/realms/example/protocol/openid-connect/auth\&#34;,&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;\&#34;issuer\&#34;: \&#34;http://localhost:8080/realms/example\&#34;,&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;\&#34;token_endpoint\&#34;: \&#34;http://localhost:8080/realms/example/protocol/openid-connect/token\&#34;,&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;\&#34;userinfo_endpoint\&#34;: \&#34;http://localhost:8080/realms/example/protocol/openid-connect/userinfo\&#34;,&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;\&#34;end_session_endpoint\&#34;: \&#34;http://localhost:8080/realms/example/protocol/openid-connect/logout\&#34;&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34;}&#34;</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@AfterAll</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">afterAll</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">mockServer</span><span class="p">.</span><span class="na">stop</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>On commence par instancier le mock server sur le port utilisé par défaut par Keycloak. Ensuite, nous utilisons le client de ce mock pour configurer le serveur. En cas de requête sur l&rsquo;endpoint well-known de Keycloak, nous demandons au mock de répondre avec un JSON contenant les principaux endpoints de la norme OIDC. Même si finalement, nous n&rsquo;utilisons que l&rsquo;endpoint <code>jwks_uri</code>, la bibliothèque Keycloak vérifie l&rsquo;existence des autres endpoints.</p> <p>Il nous faut maintenant mock l&rsquo;appel à l&rsquo;endpoint <code>jwks_uri</code> afin de permettre à la bibliothèque de Keycloak de récupérer les informations de la signature utilisé pour les tokens. Pour cela, on ajoute dans le <code>beforeAll</code>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a> </span><span class="lnt" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a> </span><span class="lnt" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a> </span><span class="lnt" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a> </span><span class="lnt" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a> </span><span class="lnt" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a> </span><span class="lnt" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a> </span><span class="lnt" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a> </span><span class="lnt" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a> </span><span class="lnt" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a> </span><span class="lnt" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a> </span><span class="lnt" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a> </span><span class="lnt" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a> </span><span class="lnt" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a> </span><span class="lnt" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a> </span><span class="lnt" id="hl-2-16"><a class="lnlinks" href="#hl-2-16">16</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">RSAKey</span><span class="w"> </span><span class="n">rsaJWK</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getRsaKey</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">X509Certificate</span><span class="w"> </span><span class="n">cert</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getX509Certificate</span><span class="p">(</span><span class="n">rsaJWK</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">getPrivateKey</span><span class="p">(</span><span class="n">rsaJWK</span><span class="p">,</span><span class="w"> </span><span class="n">cert</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">JWK</span><span class="w"> </span><span class="n">jwk</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getJwk</span><span class="p">(</span><span class="n">rsaJWK</span><span class="p">,</span><span class="w"> </span><span class="n">cert</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">mockServerClient</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">when</span><span class="p">(</span><span class="n">HttpRequest</span><span class="p">.</span><span class="na">request</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">withMethod</span><span class="p">(</span><span class="s">&#34;GET&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">withPath</span><span class="p">(</span><span class="s">&#34;/realms/example/protocol/openid-connect/certs&#34;</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">respond</span><span class="p">(</span><span class="n">HttpResponse</span><span class="p">.</span><span class="na">response</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">withStatusCode</span><span class="p">(</span><span class="n">200</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">withBody</span><span class="p">(</span><span class="n">generateKCCertsJson</span><span class="p">(</span><span class="n">jwk</span><span class="p">.</span><span class="na">toJSONString</span><span class="p">())));</span></span></span></code></pre></td></tr></table> </div> </div><h2 class="heading-element" id="génération-du-certificat-et-des-clés"><span>Génération du certificat et des clés</span> <a href="#g%c3%a9n%c3%a9ration-du-certificat-et-des-cl%c3%a9s" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Voyons à présent comment sont implémenté les méthodes <code>getRsaKey()</code>, <code>getX509Certificate(rsaJWK)</code>, <code>getPrivateKey(rsaJWK, cert)</code> et <code>getJwk(rsaJWK, cert)</code>. Pour cela, nous allons utiliser la bibliothèque <code>nimbus-jose</code> :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a class="lnlinks" href="#hl-3-1"> 1</a> </span><span class="lnt" id="hl-3-2"><a class="lnlinks" href="#hl-3-2"> 2</a> </span><span class="lnt" id="hl-3-3"><a class="lnlinks" href="#hl-3-3"> 3</a> </span><span class="lnt" id="hl-3-4"><a class="lnlinks" href="#hl-3-4"> 4</a> </span><span class="lnt" id="hl-3-5"><a class="lnlinks" href="#hl-3-5"> 5</a> </span><span class="lnt" id="hl-3-6"><a class="lnlinks" href="#hl-3-6"> 6</a> </span><span class="lnt" id="hl-3-7"><a class="lnlinks" href="#hl-3-7"> 7</a> </span><span class="lnt" id="hl-3-8"><a class="lnlinks" href="#hl-3-8"> 8</a> </span><span class="lnt" id="hl-3-9"><a class="lnlinks" href="#hl-3-9"> 9</a> </span><span class="lnt" id="hl-3-10"><a class="lnlinks" href="#hl-3-10">10</a> </span><span class="lnt" id="hl-3-11"><a class="lnlinks" href="#hl-3-11">11</a> </span><span class="lnt" id="hl-3-12"><a class="lnlinks" href="#hl-3-12">12</a> </span><span class="lnt" id="hl-3-13"><a class="lnlinks" href="#hl-3-13">13</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;dependency&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;groupId&gt;</span>com.nimbusds<span class="nt">&lt;/groupId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;artifactID&gt;</span>nimbus-jose-jwt<span class="nt">&lt;/artifactId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;version&gt;</span>9.39.1<span class="nt">&lt;/version&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/dependency&gt;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nt">&lt;dependency&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;groupId&gt;</span>com.nimbusds<span class="nt">&lt;/groupId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;artifactID&gt;</span>oauth2-oidc-sdk<span class="nt">&lt;/artifactId&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;version&gt;</span>11.12<span class="nt">&lt;/version&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/dependency&gt;</span></span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a class="lnlinks" href="#hl-4-1"> 1</a> </span><span class="lnt" id="hl-4-2"><a class="lnlinks" href="#hl-4-2"> 2</a> </span><span class="lnt" id="hl-4-3"><a class="lnlinks" href="#hl-4-3"> 3</a> </span><span class="lnt" id="hl-4-4"><a class="lnlinks" href="#hl-4-4"> 4</a> </span><span class="lnt" id="hl-4-5"><a class="lnlinks" href="#hl-4-5"> 5</a> </span><span class="lnt" id="hl-4-6"><a class="lnlinks" href="#hl-4-6"> 6</a> </span><span class="lnt" id="hl-4-7"><a class="lnlinks" href="#hl-4-7"> 7</a> </span><span class="lnt" id="hl-4-8"><a class="lnlinks" href="#hl-4-8"> 8</a> </span><span class="lnt" id="hl-4-9"><a class="lnlinks" href="#hl-4-9"> 9</a> </span><span class="lnt" id="hl-4-10"><a class="lnlinks" href="#hl-4-10">10</a> </span><span class="lnt" id="hl-4-11"><a class="lnlinks" href="#hl-4-11">11</a> </span><span class="lnt" id="hl-4-12"><a class="lnlinks" href="#hl-4-12">12</a> </span><span class="lnt" id="hl-4-13"><a class="lnlinks" href="#hl-4-13">13</a> </span><span class="lnt" id="hl-4-14"><a class="lnlinks" href="#hl-4-14">14</a> </span><span class="lnt" id="hl-4-15"><a class="lnlinks" href="#hl-4-15">15</a> </span><span class="lnt" id="hl-4-16"><a class="lnlinks" href="#hl-4-16">16</a> </span><span class="lnt" id="hl-4-17"><a class="lnlinks" href="#hl-4-17">17</a> </span><span class="lnt" id="hl-4-18"><a class="lnlinks" href="#hl-4-18">18</a> </span><span class="lnt" id="hl-4-19"><a class="lnlinks" href="#hl-4-19">19</a> </span><span class="lnt" id="hl-4-20"><a class="lnlinks" href="#hl-4-20">20</a> </span><span class="lnt" id="hl-4-21"><a class="lnlinks" href="#hl-4-21">21</a> </span><span class="lnt" id="hl-4-22"><a class="lnlinks" href="#hl-4-22">22</a> </span><span class="lnt" id="hl-4-23"><a class="lnlinks" href="#hl-4-23">23</a> </span><span class="lnt" id="hl-4-24"><a class="lnlinks" href="#hl-4-24">24</a> </span><span class="lnt" id="hl-4-25"><a class="lnlinks" href="#hl-4-25">25</a> </span><span class="lnt" id="hl-4-26"><a class="lnlinks" href="#hl-4-26">26</a> </span><span class="lnt" id="hl-4-27"><a class="lnlinks" href="#hl-4-27">27</a> </span><span class="lnt" id="hl-4-28"><a class="lnlinks" href="#hl-4-28">28</a> </span><span class="lnt" id="hl-4-29"><a class="lnlinks" href="#hl-4-29">29</a> </span><span class="lnt" id="hl-4-30"><a class="lnlinks" href="#hl-4-30">30</a> </span><span class="lnt" id="hl-4-31"><a class="lnlinks" href="#hl-4-31">31</a> </span><span class="lnt" id="hl-4-32"><a class="lnlinks" href="#hl-4-32">32</a> </span><span class="lnt" id="hl-4-33"><a class="lnlinks" href="#hl-4-33">33</a> </span><span class="lnt" id="hl-4-34"><a class="lnlinks" href="#hl-4-34">34</a> </span><span class="lnt" id="hl-4-35"><a class="lnlinks" href="#hl-4-35">35</a> </span><span class="lnt" id="hl-4-36"><a class="lnlinks" href="#hl-4-36">36</a> </span><span class="lnt" id="hl-4-37"><a class="lnlinks" href="#hl-4-37">37</a> </span><span class="lnt" id="hl-4-38"><a class="lnlinks" href="#hl-4-38">38</a> </span><span class="lnt" id="hl-4-39"><a class="lnlinks" href="#hl-4-39">39</a> </span><span class="lnt" id="hl-4-40"><a class="lnlinks" href="#hl-4-40">40</a> </span><span class="lnt" id="hl-4-41"><a class="lnlinks" href="#hl-4-41">41</a> </span><span class="lnt" id="hl-4-42"><a class="lnlinks" href="#hl-4-42">42</a> </span><span class="lnt" id="hl-4-43"><a class="lnlinks" href="#hl-4-43">43</a> </span><span class="lnt" id="hl-4-44"><a class="lnlinks" href="#hl-4-44">44</a> </span><span class="lnt" id="hl-4-45"><a class="lnlinks" href="#hl-4-45">45</a> </span><span class="lnt" id="hl-4-46"><a class="lnlinks" href="#hl-4-46">46</a> </span><span class="lnt" id="hl-4-47"><a class="lnlinks" href="#hl-4-47">47</a> </span><span class="lnt" id="hl-4-48"><a class="lnlinks" href="#hl-4-48">48</a> </span><span class="lnt" id="hl-4-49"><a class="lnlinks" href="#hl-4-49">49</a> </span><span class="lnt" id="hl-4-50"><a class="lnlinks" href="#hl-4-50">50</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Create a JWK with a random ID </span></span></span><span class="line"><span class="cl"><span class="cm"> * </span></span></span><span class="line"><span class="cl"><span class="cm"> * @return The new JSON Web Key with 2048 RSA key </span></span></span><span class="line"><span class="cl"><span class="cm"> * @throws JOSEException Something wrong happen during RSA key generation </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">RSAKey</span><span class="w"> </span><span class="nf">getRsaKey</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">JOSEException</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RSAKeyGenerator</span><span class="p">(</span><span class="n">2048</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">keyID</span><span class="p">(</span><span class="n">OIDCFilterTest</span><span class="p">.</span><span class="na">KEY_ID</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">keyUse</span><span class="p">(</span><span class="n">KeyUse</span><span class="p">.</span><span class="na">SIGNATURE</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">generate</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Generate a self signed certificate </span></span></span><span class="line"><span class="cl"><span class="cm"> * @param rsaJWK The Json Web Key </span></span></span><span class="line"><span class="cl"><span class="cm"> * @return A self-signed certificate. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">X509Certificate</span><span class="w"> </span><span class="nf">getX509Certificate</span><span class="p">(</span><span class="n">RSAKey</span><span class="w"> </span><span class="n">rsaJWK</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">OperatorCreationException</span><span class="p">,</span><span class="w"> </span><span class="n">IOException</span><span class="p">,</span><span class="w"> </span><span class="n">JOSEException</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">X509CertificateUtils</span><span class="p">.</span><span class="na">generateSelfSigned</span><span class="p">(</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Issuer</span><span class="p">(</span><span class="s">&#34;http://localhost:8080&#34;</span><span class="p">),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Date</span><span class="p">.</span><span class="na">from</span><span class="p">(</span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">minus</span><span class="p">(</span><span class="n">1</span><span class="p">,</span><span class="w"> </span><span class="n">ChronoUnit</span><span class="p">.</span><span class="na">DAYS</span><span class="p">)),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Date</span><span class="p">.</span><span class="na">from</span><span class="p">(</span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">plus</span><span class="p">(</span><span class="n">1</span><span class="p">,</span><span class="w"> </span><span class="n">ChronoUnit</span><span class="p">.</span><span class="na">DAYS</span><span class="p">)),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">rsaJWK</span><span class="p">.</span><span class="na">toRSAPublicKey</span><span class="p">(),</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">rsaJWK</span><span class="p">.</span><span class="na">toRSAPrivateKey</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@SuppressWarnings</span><span class="p">(</span><span class="s">&#34;deprecation&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">getPrivateKey</span><span class="p">(</span><span class="n">RSAKey</span><span class="w"> </span><span class="n">rsaJWK</span><span class="p">,</span><span class="w"> </span><span class="n">X509Certificate</span><span class="w"> </span><span class="n">cert</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">CertificateEncodingException</span><span class="p">,</span><span class="w"> </span><span class="n">JOSEException</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Create private key from the certificate</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">privateJWK</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RSAKey</span><span class="p">.</span><span class="na">Builder</span><span class="p">(</span><span class="n">rsaJWK</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">x509CertChain</span><span class="p">(</span><span class="n">Collections</span><span class="p">.</span><span class="na">singletonList</span><span class="p">(</span><span class="n">Base64</span><span class="p">.</span><span class="na">encode</span><span class="p">(</span><span class="n">cert</span><span class="p">.</span><span class="na">getEncoded</span><span class="p">())))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">x509CertThumbprint</span><span class="p">(</span><span class="n">rsaJWK</span><span class="p">.</span><span class="na">computeThumbprint</span><span class="p">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">build</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nd">@SuppressWarnings</span><span class="p">(</span><span class="s">&#34;deprecation&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">JWK</span><span class="w"> </span><span class="nf">getJwk</span><span class="p">(</span><span class="n">RSAKey</span><span class="w"> </span><span class="n">rsaJWK</span><span class="p">,</span><span class="w"> </span><span class="n">X509Certificate</span><span class="w"> </span><span class="n">cert</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">CertificateEncodingException</span><span class="p">,</span><span class="w"> </span><span class="n">JOSEException</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RSAKey</span><span class="p">.</span><span class="na">Builder</span><span class="p">(</span><span class="n">rsaJWK</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">keyUse</span><span class="p">(</span><span class="n">KeyUse</span><span class="p">.</span><span class="na">SIGNATURE</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">keyID</span><span class="p">(</span><span class="n">KEY_ID</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">issueTime</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">Date</span><span class="p">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">algorithm</span><span class="p">(</span><span class="n">algorithm</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">x509CertChain</span><span class="p">(</span><span class="n">Collections</span><span class="p">.</span><span class="na">singletonList</span><span class="p">(</span><span class="n">Base64</span><span class="p">.</span><span class="na">encode</span><span class="p">(</span><span class="n">cert</span><span class="p">.</span><span class="na">getEncoded</span><span class="p">())))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">x509CertThumbprint</span><span class="p">(</span><span class="n">rsaJWK</span><span class="p">.</span><span class="na">computeThumbprint</span><span class="p">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">x509CertSHA256Thumbprint</span><span class="p">(</span><span class="n">rsaJWK</span><span class="p">.</span><span class="na">computeThumbprint</span><span class="p">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">build</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Le code est assez parlant de lui-même. On commence par générer un combo clé privée/publique en RSA 2048. Nous utilisons ces clés pour générer un certificat X509 auto signé. On utilise ensuite ce certificat pour générer des clés asymétriques pour la signature des tokens.</p> <p>À noter que le certificat X509 à une validité de 1 jour ici. Pour des tests, cela est amplement suffisant. Nous ajoutons le paramètre <code>x509CertThumbprint</code> qui est déprécié, car Keycloak le génère également.</p> <p>Il nous reste à exposer ces informations au format JSON. La méthode <code>generateKCCErtsJSON</code> s&rsquo;en charge :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a class="lnlinks" href="#hl-5-1">1</a> </span><span class="lnt" id="hl-5-2"><a class="lnlinks" href="#hl-5-2">2</a> </span><span class="lnt" id="hl-5-3"><a class="lnlinks" href="#hl-5-3">3</a> </span><span class="lnt" id="hl-5-4"><a class="lnlinks" href="#hl-5-4">4</a> </span><span class="lnt" id="hl-5-5"><a class="lnlinks" href="#hl-5-5">5</a> </span><span class="lnt" id="hl-5-6"><a class="lnlinks" href="#hl-5-6">6</a> </span><span class="lnt" id="hl-5-7"><a class="lnlinks" href="#hl-5-7">7</a> </span><span class="lnt" id="hl-5-8"><a class="lnlinks" href="#hl-5-8">8</a> </span><span class="lnt" id="hl-5-9"><a class="lnlinks" href="#hl-5-9">9</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">generateKCCertsJson</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">JSONObject</span><span class="w"> </span><span class="n">keyObject</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">JSONObject</span><span class="p">(</span><span class="n">key</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">JSONObject</span><span class="w"> </span><span class="n">root</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">JSONObject</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">JSONArray</span><span class="w"> </span><span class="n">keys</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">JSONArray</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">keys</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="n">keyObject</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">root</span><span class="p">.</span><span class="na">put</span><span class="p">(</span><span class="s">&#34;keys&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">keys</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">root</span><span class="p">.</span><span class="na">toString</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Keycloak pouvant exposer plusieurs clés, la méthode se contente de créer un tableau <code>keys</code> dans lequel sont insérées les informations générées précédemment.</p> <h2 class="heading-element" id="génération-du-token"><span>Génération du token</span> <a href="#g%c3%a9n%c3%a9ration-du-token" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Il ne nous reste plus qu&rsquo;à générer un JWT signé par notre clé issue du certificat X509 exposé par notre mockServer.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a class="lnlinks" href="#hl-6-1"> 1</a> </span><span class="lnt" id="hl-6-2"><a class="lnlinks" href="#hl-6-2"> 2</a> </span><span class="lnt" id="hl-6-3"><a class="lnlinks" href="#hl-6-3"> 3</a> </span><span class="lnt" id="hl-6-4"><a class="lnlinks" href="#hl-6-4"> 4</a> </span><span class="lnt" id="hl-6-5"><a class="lnlinks" href="#hl-6-5"> 5</a> </span><span class="lnt" id="hl-6-6"><a class="lnlinks" href="#hl-6-6"> 6</a> </span><span class="lnt" id="hl-6-7"><a class="lnlinks" href="#hl-6-7"> 7</a> </span><span class="lnt" id="hl-6-8"><a class="lnlinks" href="#hl-6-8"> 8</a> </span><span class="lnt" id="hl-6-9"><a class="lnlinks" href="#hl-6-9"> 9</a> </span><span class="lnt" id="hl-6-10"><a class="lnlinks" href="#hl-6-10">10</a> </span><span class="lnt" id="hl-6-11"><a class="lnlinks" href="#hl-6-11">11</a> </span><span class="lnt" id="hl-6-12"><a class="lnlinks" href="#hl-6-12">12</a> </span><span class="lnt" id="hl-6-13"><a class="lnlinks" href="#hl-6-13">13</a> </span><span class="lnt" id="hl-6-14"><a class="lnlinks" href="#hl-6-14">14</a> </span><span class="lnt" id="hl-6-15"><a class="lnlinks" href="#hl-6-15">15</a> </span><span class="lnt" id="hl-6-16"><a class="lnlinks" href="#hl-6-16">16</a> </span><span class="lnt" id="hl-6-17"><a class="lnlinks" href="#hl-6-17">17</a> </span><span class="lnt" id="hl-6-18"><a class="lnlinks" href="#hl-6-18">18</a> </span><span class="lnt" id="hl-6-19"><a class="lnlinks" href="#hl-6-19">19</a> </span><span class="lnt" id="hl-6-20"><a class="lnlinks" href="#hl-6-20">20</a> </span><span class="lnt" id="hl-6-21"><a class="lnlinks" href="#hl-6-21">21</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Create RSA-signer with the private key</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">JWSSigner</span><span class="w"> </span><span class="n">signer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RSASSASigner</span><span class="p">(</span><span class="n">privateJWK</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">JWSHeader</span><span class="w"> </span><span class="n">header</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Builder</span><span class="p">(</span><span class="n">JWSAlgorithm</span><span class="p">.</span><span class="na">RS256</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">type</span><span class="p">(</span><span class="n">JOSEObjectType</span><span class="p">.</span><span class="na">JWT</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">keyID</span><span class="p">(</span><span class="n">KEY_ID</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">build</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">JWTClaimsSet</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">JWTClaimsSet</span><span class="p">.</span><span class="na">Builder</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">issuer</span><span class="p">(</span><span class="s">&#34;http://localhost:8080/realms/example&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">audience</span><span class="p">(</span><span class="s">&#34;myService&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">subject</span><span class="p">(</span><span class="s">&#34;myApp&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">claim</span><span class="p">(</span><span class="s">&#34;typ&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Bearer&#34;</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">expirationTime</span><span class="p">(</span><span class="n">Date</span><span class="p">.</span><span class="na">from</span><span class="p">(</span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">plusSeconds</span><span class="p">(</span><span class="n">300</span><span class="p">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">notBeforeTime</span><span class="p">(</span><span class="n">Date</span><span class="p">.</span><span class="na">from</span><span class="p">(</span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">minusSeconds</span><span class="p">(</span><span class="n">120</span><span class="p">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="na">build</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">JWSObject</span><span class="w"> </span><span class="n">jwsObject</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">JWSObject</span><span class="p">(</span><span class="n">header</span><span class="p">,</span><span class="w"> </span><span class="n">payload</span><span class="p">.</span><span class="na">toPayload</span><span class="p">());</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Compute the RSA signature</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">jwsObject</span><span class="p">.</span><span class="na">sign</span><span class="p">(</span><span class="n">signer</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">jwsObject</span><span class="p">.</span><span class="na">serialize</span><span class="p">();</span></span></span></code></pre></td></tr></table> </div> </div><p>Le fichier de configuration Keycloak <code>keycloak.json</code> permettant de valider ce token est le suivant :</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-7-1"><a class="lnlinks" href="#hl-7-1">1</a> </span><span class="lnt" id="hl-7-2"><a class="lnlinks" href="#hl-7-2">2</a> </span><span class="lnt" id="hl-7-3"><a class="lnlinks" href="#hl-7-3">3</a> </span><span class="lnt" id="hl-7-4"><a class="lnlinks" href="#hl-7-4">4</a> </span><span class="lnt" id="hl-7-5"><a class="lnlinks" href="#hl-7-5">5</a> </span><span class="lnt" id="hl-7-6"><a class="lnlinks" href="#hl-7-6">6</a> </span><span class="lnt" id="hl-7-7"><a class="lnlinks" href="#hl-7-7">7</a> </span><span class="lnt" id="hl-7-8"><a class="lnlinks" href="#hl-7-8">8</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;realms&#34;</span><span class="p">:</span> <span class="s2">&#34;example&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;auth-server-url&#34;</span><span class="p">:</span> <span class="s2">&#34;http://localhost:8080&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;ssl-required&#34;</span><span class="p">:</span> <span class="s2">&#34;external&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;resource&#34;</span><span class="p">:</span> <span class="s2">&#34;myService&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;verify-token-audience&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="err">...</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><h2 class="heading-element" id="conclusion"><span>Conclusion</span> <a href="#conclusion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>C&rsquo;est terminer. Cela fait pas mal de code pour juste signer et validé un jeton. La bibliothèque <code>nimbus</code> nous aide fortement dans cette tâche. Les tests de sécurité sont maintenant beaucoup plus rapides à exécuter. Même si nous démarrons un serveur netty, cela n&rsquo;est rien comparé à un serveur Keycloak dans une image docker. Petit bonus, le serveur mock permet en plus de voir l&rsquo;ensemble des requêtes exécutées avec leurs paramètres. Pratique pour débugguer.</p> Quarkus Extension Additional Application Archive Marker Build Item https://techland.info/posts/quarkus/quarkus-extension-additional-application-archive-marker-build-item/ Thu, 29 Feb 2024 11:23:22 +0100 https://techland.info/posts/quarkus/quarkus-extension-additional-application-archive-marker-build-item/ Développement <p>On continue avec les différentes manières d&rsquo;indexer des Beans via une extension Quarkus avec l&rsquo;<code>AdditionalApplicationArchiveMarkerBuildItem</code>. Ce build item permet en quelques sorts de faire en une seule étape ce que fait la combinaison <code>CombinedIndexBuildItem</code> et <code>AdditionalBeanBuildItem</code>.</p> <p>Comme son nom l&rsquo;indique ce build item permet d&rsquo;indiquer à Quarkus des fichiers <code>Marker</code> supplémentaire à prendre en compte au moment du scan du classpath. Par défaut Quarkus va indexer l&rsquo;ensemble des Beans dans les archives possédant un fichier <code>beans.xml</code>, <code>jandex.idx</code> ou <code>config properties</code>. Ce build item permet de déclarer des fichiers <code>Marker</code> supplémentaire pour déclencher l&rsquo;indexation.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a> </span><span class="lnt" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a> </span><span class="lnt" id="hl-0-3"><a class="lnlinks" href="#hl-0-3">3</a> </span><span class="lnt" id="hl-0-4"><a class="lnlinks" href="#hl-0-4">4</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@BuildStep</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="w"> </span><span class="nf">addMarker</span><span class="p">(</span><span class="n">BuildProducer</span><span class="o">&lt;</span><span class="n">AdditionalApplicationArchiveMarkerBuildItem</span><span class="o">&gt;</span><span class="w"> </span><span class="n">producer</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">producer</span><span class="p">.</span><span class="na">produce</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">AdditionalApplicationArchiveMarkerBuildItem</span><span class="p">(</span><span class="s">&#34;my/package/Foo/markerFile.ext&#34;</span><span class="p">));</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>Dans cet exemple, Quarkus va indexer l&rsquo;ensemble des beans contenu dans les archives ayant le fichier <code>my/package/Foo/markerFile.ext</code>. On utilise ici la notation <code>/</code> et non celle pointée<code>(.)</code> pour les packages. Le nom d&rsquo;un package peu aussi est spécifié.</p> <h2 class="heading-element" id="conclusion"><span>Conclusion</span> <a href="#conclusion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>L&rsquo;<code>AdditionalApplicationArchiveMarkerBuildItem</code> est un build item très puissant, mais à utiliser avec parcimonie. En effet, il peut très rapidement conduire à l&rsquo;inclusion de Bean non voulu dans l&rsquo;index Quarkus. De ce point de vue le combo <code>CombinedIndexBuildItem</code> et <code>AdditionalBeanBuildItem</code> offre plus de contrôle.</p>