08-12-2023, 08:49 AM
Managing JBoss EAP/Wildfly using Jcliff
<div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/01/managing-jboss-eap-wildfly-using-jcliff.png" width="16" height="16" title="" alt="" /></div><div><p>Systems management can be a difficult task. Not only does one need to determine what the end state should be but, more importantly, how to ensure systems attain and remain at this state. Doing so in an automated fashion is just as critical, because there may be a large number of target instances. In regard to enterprise <a href="https://developers.redhat.com/topics/enterprise-java/">Java</a> middleware application servers, these instances are typically configured using a set of XML based files. Although these files may be manually configured, most application servers have a command-line based tool or set of tools that abstracts the end user from having to worry about the underlying configuration. WebSphere Liberty includes a variety of tools to manage these resources, whereas <a href="https://developers.redhat.com/products/eap/overview">JBoss</a> contains the <a href="https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.2/html/management_cli_guide/index" target="_blank" rel="noopener noreferrer">jboss-cli</a> tool.</p>
<p>Although each tool accomplishes its utilitarian use case as it allows for proper server management, it does fail to adhere to one of the principles of automation and configuration management: idempotence. Ensuring the desired state does not equate to executing the same action with every iteration. Additional intelligence must be introduced. Along with idempotence, another core principle of configuration management is that values be expressed declaratively and stored in a version control system.</p>
<p><a href="https://github.com/bserdar/jcliff" target="_blank" rel="noopener noreferrer">Jcliff</a> is a Java-based utility that is built on top of the JBoss command-line interface and allows for the desired intent for the server configuration to be expressed declaratively, which in turn can be stored in a version control system. We’ll provide an overview of the Jcliff utility including inherent benefits, installation options, and several examples showcasing the use.<span id="more-644807"></span></p>
<h2>The problem space</h2>
<p>Before beginning to work with Jcliff, one must be cognizant of the underlying JBoss architecture. The configuration of the JBoss server can be expressed using Dynamic Model Representation (DMR) notation. The following is an example of DMR:</p>
<pre>{ "system-property" => { "foo" => "bar", "bah" => "gah" } } </pre>
<p>The DMR example above describes several Java system properties that will be stored within the JBoss configuration. These properties can be added using the JBoss CLI by executing the following command:</p>
<pre>/system-property=foo:add(value=bar) /system-property=bah:add(value=gah) </pre>
<p>The challenge is that the same commands cannot be executed more than once. Otherwise, an error similar to the following is produced.</p>
<pre>{ "outcome" => "failed", "failure-description" => "WFLYCTL0212: Duplicate resource [(\"system-property\" => \"foo\")]", "rolled-back" => true } </pre>
<p>Where Jcliff excels is that it includes the necessary intelligence to determine the current state of the JBoss configuration and then applying the appropriate configurations necessary. This is critical to adopting proper configuration management when working with JBoss.</p>
<h2>Installing Jcliff</h2>
<p>With a baseline understanding of Jcliff, let’s discuss the methods in which one can obtain the utility. First, Jcliff can be built from source from the <a href="https://github.com/bserdar/jcliff" target="_blank" rel="noopener noreferrer">project repository</a>, given that it’s a freely open source project. Alternatively, Jcliff can be installed using popular software packaging tools in each of the primary operating system families.</p>
<h3>Linux (rpm)</h3>
<p>Jcliff can be consumed on a Linux package when capable of consuming an rpm using a package manager.</p>
<p>First, install the yum repository:</p>
<pre>$ cat /etc/yum.repos.d/jcliff.repo [jcliff] baseurl = http://people.redhat.com/~rpelisse/jcliff.yum/ gpgcheck = 0 name = JCliff repository </pre>
<p>Once this repository has been added, JCliff can be installed by using <a href="http://yum.baseurl.org/" target="_blank" rel="noopener noreferrer">Yum</a> or <a href="https://fedoraproject.org/wiki/DNF?rd=Dnf" target="_blank" rel="noopener noreferrer">Dnf</a>:</p>
<p>Using yum:</p>
<pre>$ sudo yum install jcliff </pre>
<p>Or using dnf:</p>
<pre>$ sudo dnf install jcliff </pre>
<h3>Manual installation</h3>
<p>Jcliff can also be installed manually without the need to leverage a package manager. This is useful for those on Linux distributions that cannot consume rpm packages. Simply download and unpack the archive from the <a href="https://github.com/bserdar/jcliff/releases/download/v2.12.1/jcliff-2.12.1-dist.tar.gz" target="_blank" rel="noopener noreferrer">release artifact</a> from the project repository.</p>
<pre>$ curl -O \ https://github.com/bserdar/jcliff/releas...ist.tar.gz $ tar xzvf jcliff-2.12.1-dist.tar.gz </pre>
<p>Place the resulting Jcliff folder in the destination of your choosing. This location is known as JCLIFF_HOME. Add this environment variable and add it to the path as described below:</p>
<pre>$ export JCLIFF_HOME=/path/to/jcliff-2.12.1/ $ export PATH=${JCLIFF_HOME}/bin:${PATH} </pre>
<p>At this point, you should be able to execute the jcliff command.</p>
<h3>OSX</h3>
<p>While users on OSX can take advantage of the manual installation option, those making use of the brew package manager can use this tool as well.</p>
<p>Execute the following commands to install Jcliff using Brew:</p>
<pre>$ brew tap redhat-cop/redhat-cop $ brew install redhat-cop/redhat-cop/jcliff </pre>
<h3>Windows</h3>
<p>Fear not, Windows users; you can also make use of Jcliff without having to compile from source. Windows, in the same vein as OSX, does not have an official package manager, but <a href="https://chocolatey.org/" target="_blank" rel="noopener noreferrer">Chocolatey</a> has been given this role.</p>
<p>Execute the following command to install Jcliff using Chocolatey:</p>
<pre>$ choco install jcliff </pre>
<h2>Using Jcliff</h2>
<p>With Jcliff properly installed on your machine, let’s walk through a simple example to demonstrate the use of the tool. As discussed previously, Jcliff makes use of files that describe the target configuration. At this point, you may have questions like: What is the format of these configurations, and where do I begin?</p>
<p>Let’s take a simple example and look into adding a new system property to the JBoss configuration. Launch an instance of JBoss and connect using the JBoss command-line interface:</p>
<pre>$ /bin/jboss-cli.sh --connect [standalone@localhost:9990 /] ls /system-property [standalone@localhost:9990 /] </pre>
<p>Now, use Jcliff to update the configuration. First, we’ll need to create a Jcliff rule. The rule resembles DMR format and appears similar to the following:</p>
<pre>$ cat jcliff-prop { "system-property" => { "jcliff.enabled" => "true", } } </pre>
<p>Then we can simply ask JCliff to run this script against our JBoss server:</p>
<pre>$ jcliff jcliff-prop Jcliff version 2.12.4 2019-10-02 17:03:40:0008: /core-service=platform-mbean/type=runtime:read-attribute(name=system-properties) 2019-10-02 17:03:41:0974: /system-property=jcliff.enabled:add(value="true") </pre>
<p>Use the JBoss CLI to verify the changes have been applied.</p>
<pre>$ “${JBOSS_HOME}/bin/jboss-cli.sh” --connect --command=/system-property=jcliff.enabled:read-resource { "outcome" => "success", "result" => {"value" => "true"} } </pre>
<p>To demonstrate how Jcliff handles repetitive executions, run the previous Jcliff command again. Inspect the output.</p>
<pre>$ jcliff jcliff-prop Jcliff version 2.12.4 2019-10-02 17:05:34:0107: /core-service=platform-mbean/type=runtime:read-attribute(name=system-properties) </pre>
<p>Notice that only 1 command was executed against the JBoss server instead of two? This action was a result of assessing the current state within JBoss and determining that no action was necessary to reach the desired state. Mission accomplished.</p>
<h2>Next steps</h2>
<p>Although the preceding scenario was not overly complex, you should now have the knowledge necessary to understand the capabilities and functionality of the Jcliff utility as well as the benefits afforded through declarative configurations and automation. When building out enterprise-grade systems, however, Jcliff would not be executed manually. You would want to integrate the utility into a proper configuration management tool that employs many of the automation and configuration principles described earlier. Fortunately, Jcliff has been integrated into several popular configuration management tools.</p>
<p>In an upcoming article, we’ll provide an overview of the configuration management options available with Jcliff, along with examples that will allow you to quickly build out enterprise-grade confidence with Jcliff.</p>
<p><a class="a2a_button_facebook" href="https://www.addtoany.com/add_to/facebook?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Facebook" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_twitter" href="https://www.addtoany.com/add_to/twitter?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Twitter" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="LinkedIn" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Email" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_print" href="https://www.addtoany.com/add_to/print?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Print" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Reddit" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_flipboard" href="https://www.addtoany.com/add_to/flipboard?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Flipboard" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&title=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" data-a2a-url="https://developers.redhat.com/blog/2019/11/06/managing-jboss-eap-wildfly-using-jcliff/" data-a2a-title="Managing JBoss EAP/Wildfly using Jcliff"><img decoding="async" src="https://static.addtoany.com/buttons/favicon.png" alt="Share"></a></p>
<p>The post <a rel="nofollow" href="https://developers.redhat.com/blog/2019/11/06/managing-jboss-eap-wildfly-using-jcliff/">Managing JBoss EAP/Wildfly using Jcliff</a> appeared first on <a rel="nofollow" href="https://developers.redhat.com/blog">Red Hat Developer</a>.</p>
</div>
https://www.sickgaming.net/blog/2019/11/...ng-jcliff/
<div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/01/managing-jboss-eap-wildfly-using-jcliff.png" width="16" height="16" title="" alt="" /></div><div><p>Systems management can be a difficult task. Not only does one need to determine what the end state should be but, more importantly, how to ensure systems attain and remain at this state. Doing so in an automated fashion is just as critical, because there may be a large number of target instances. In regard to enterprise <a href="https://developers.redhat.com/topics/enterprise-java/">Java</a> middleware application servers, these instances are typically configured using a set of XML based files. Although these files may be manually configured, most application servers have a command-line based tool or set of tools that abstracts the end user from having to worry about the underlying configuration. WebSphere Liberty includes a variety of tools to manage these resources, whereas <a href="https://developers.redhat.com/products/eap/overview">JBoss</a> contains the <a href="https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.2/html/management_cli_guide/index" target="_blank" rel="noopener noreferrer">jboss-cli</a> tool.</p>
<p>Although each tool accomplishes its utilitarian use case as it allows for proper server management, it does fail to adhere to one of the principles of automation and configuration management: idempotence. Ensuring the desired state does not equate to executing the same action with every iteration. Additional intelligence must be introduced. Along with idempotence, another core principle of configuration management is that values be expressed declaratively and stored in a version control system.</p>
<p><a href="https://github.com/bserdar/jcliff" target="_blank" rel="noopener noreferrer">Jcliff</a> is a Java-based utility that is built on top of the JBoss command-line interface and allows for the desired intent for the server configuration to be expressed declaratively, which in turn can be stored in a version control system. We’ll provide an overview of the Jcliff utility including inherent benefits, installation options, and several examples showcasing the use.<span id="more-644807"></span></p>
<h2>The problem space</h2>
<p>Before beginning to work with Jcliff, one must be cognizant of the underlying JBoss architecture. The configuration of the JBoss server can be expressed using Dynamic Model Representation (DMR) notation. The following is an example of DMR:</p>
<pre>{ "system-property" => { "foo" => "bar", "bah" => "gah" } } </pre>
<p>The DMR example above describes several Java system properties that will be stored within the JBoss configuration. These properties can be added using the JBoss CLI by executing the following command:</p>
<pre>/system-property=foo:add(value=bar) /system-property=bah:add(value=gah) </pre>
<p>The challenge is that the same commands cannot be executed more than once. Otherwise, an error similar to the following is produced.</p>
<pre>{ "outcome" => "failed", "failure-description" => "WFLYCTL0212: Duplicate resource [(\"system-property\" => \"foo\")]", "rolled-back" => true } </pre>
<p>Where Jcliff excels is that it includes the necessary intelligence to determine the current state of the JBoss configuration and then applying the appropriate configurations necessary. This is critical to adopting proper configuration management when working with JBoss.</p>
<h2>Installing Jcliff</h2>
<p>With a baseline understanding of Jcliff, let’s discuss the methods in which one can obtain the utility. First, Jcliff can be built from source from the <a href="https://github.com/bserdar/jcliff" target="_blank" rel="noopener noreferrer">project repository</a>, given that it’s a freely open source project. Alternatively, Jcliff can be installed using popular software packaging tools in each of the primary operating system families.</p>
<h3>Linux (rpm)</h3>
<p>Jcliff can be consumed on a Linux package when capable of consuming an rpm using a package manager.</p>
<p>First, install the yum repository:</p>
<pre>$ cat /etc/yum.repos.d/jcliff.repo [jcliff] baseurl = http://people.redhat.com/~rpelisse/jcliff.yum/ gpgcheck = 0 name = JCliff repository </pre>
<p>Once this repository has been added, JCliff can be installed by using <a href="http://yum.baseurl.org/" target="_blank" rel="noopener noreferrer">Yum</a> or <a href="https://fedoraproject.org/wiki/DNF?rd=Dnf" target="_blank" rel="noopener noreferrer">Dnf</a>:</p>
<p>Using yum:</p>
<pre>$ sudo yum install jcliff </pre>
<p>Or using dnf:</p>
<pre>$ sudo dnf install jcliff </pre>
<h3>Manual installation</h3>
<p>Jcliff can also be installed manually without the need to leverage a package manager. This is useful for those on Linux distributions that cannot consume rpm packages. Simply download and unpack the archive from the <a href="https://github.com/bserdar/jcliff/releases/download/v2.12.1/jcliff-2.12.1-dist.tar.gz" target="_blank" rel="noopener noreferrer">release artifact</a> from the project repository.</p>
<pre>$ curl -O \ https://github.com/bserdar/jcliff/releas...ist.tar.gz $ tar xzvf jcliff-2.12.1-dist.tar.gz </pre>
<p>Place the resulting Jcliff folder in the destination of your choosing. This location is known as JCLIFF_HOME. Add this environment variable and add it to the path as described below:</p>
<pre>$ export JCLIFF_HOME=/path/to/jcliff-2.12.1/ $ export PATH=${JCLIFF_HOME}/bin:${PATH} </pre>
<p>At this point, you should be able to execute the jcliff command.</p>
<h3>OSX</h3>
<p>While users on OSX can take advantage of the manual installation option, those making use of the brew package manager can use this tool as well.</p>
<p>Execute the following commands to install Jcliff using Brew:</p>
<pre>$ brew tap redhat-cop/redhat-cop $ brew install redhat-cop/redhat-cop/jcliff </pre>
<h3>Windows</h3>
<p>Fear not, Windows users; you can also make use of Jcliff without having to compile from source. Windows, in the same vein as OSX, does not have an official package manager, but <a href="https://chocolatey.org/" target="_blank" rel="noopener noreferrer">Chocolatey</a> has been given this role.</p>
<p>Execute the following command to install Jcliff using Chocolatey:</p>
<pre>$ choco install jcliff </pre>
<h2>Using Jcliff</h2>
<p>With Jcliff properly installed on your machine, let’s walk through a simple example to demonstrate the use of the tool. As discussed previously, Jcliff makes use of files that describe the target configuration. At this point, you may have questions like: What is the format of these configurations, and where do I begin?</p>
<p>Let’s take a simple example and look into adding a new system property to the JBoss configuration. Launch an instance of JBoss and connect using the JBoss command-line interface:</p>
<pre>$ /bin/jboss-cli.sh --connect [standalone@localhost:9990 /] ls /system-property [standalone@localhost:9990 /] </pre>
<p>Now, use Jcliff to update the configuration. First, we’ll need to create a Jcliff rule. The rule resembles DMR format and appears similar to the following:</p>
<pre>$ cat jcliff-prop { "system-property" => { "jcliff.enabled" => "true", } } </pre>
<p>Then we can simply ask JCliff to run this script against our JBoss server:</p>
<pre>$ jcliff jcliff-prop Jcliff version 2.12.4 2019-10-02 17:03:40:0008: /core-service=platform-mbean/type=runtime:read-attribute(name=system-properties) 2019-10-02 17:03:41:0974: /system-property=jcliff.enabled:add(value="true") </pre>
<p>Use the JBoss CLI to verify the changes have been applied.</p>
<pre>$ “${JBOSS_HOME}/bin/jboss-cli.sh” --connect --command=/system-property=jcliff.enabled:read-resource { "outcome" => "success", "result" => {"value" => "true"} } </pre>
<p>To demonstrate how Jcliff handles repetitive executions, run the previous Jcliff command again. Inspect the output.</p>
<pre>$ jcliff jcliff-prop Jcliff version 2.12.4 2019-10-02 17:05:34:0107: /core-service=platform-mbean/type=runtime:read-attribute(name=system-properties) </pre>
<p>Notice that only 1 command was executed against the JBoss server instead of two? This action was a result of assessing the current state within JBoss and determining that no action was necessary to reach the desired state. Mission accomplished.</p>
<h2>Next steps</h2>
<p>Although the preceding scenario was not overly complex, you should now have the knowledge necessary to understand the capabilities and functionality of the Jcliff utility as well as the benefits afforded through declarative configurations and automation. When building out enterprise-grade systems, however, Jcliff would not be executed manually. You would want to integrate the utility into a proper configuration management tool that employs many of the automation and configuration principles described earlier. Fortunately, Jcliff has been integrated into several popular configuration management tools.</p>
<p>In an upcoming article, we’ll provide an overview of the configuration management options available with Jcliff, along with examples that will allow you to quickly build out enterprise-grade confidence with Jcliff.</p>
<p><a class="a2a_button_facebook" href="https://www.addtoany.com/add_to/facebook?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Facebook" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_twitter" href="https://www.addtoany.com/add_to/twitter?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Twitter" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="LinkedIn" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Email" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_print" href="https://www.addtoany.com/add_to/print?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Print" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Reddit" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_button_flipboard" href="https://www.addtoany.com/add_to/flipboard?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&linkname=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" title="Flipboard" rel="nofollow noopener noreferrer" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F06%2Fmanaging-jboss-eap-wildfly-using-jcliff%2F&title=Managing%20JBoss%20EAP%2FWildfly%20using%20Jcliff" data-a2a-url="https://developers.redhat.com/blog/2019/11/06/managing-jboss-eap-wildfly-using-jcliff/" data-a2a-title="Managing JBoss EAP/Wildfly using Jcliff"><img decoding="async" src="https://static.addtoany.com/buttons/favicon.png" alt="Share"></a></p>
<p>The post <a rel="nofollow" href="https://developers.redhat.com/blog/2019/11/06/managing-jboss-eap-wildfly-using-jcliff/">Managing JBoss EAP/Wildfly using Jcliff</a> appeared first on <a rel="nofollow" href="https://developers.redhat.com/blog">Red Hat Developer</a>.</p>
</div>
https://www.sickgaming.net/blog/2019/11/...ng-jcliff/