How to manage WildFly CLI execution with JCliff

In this tutorial we will learn how to use the JCliff tool to improve the management of WildFly application server using the CLI

WildFly CLI is a powerful instrument to manage the application server resources using Dynamic Model Representation (DMR) notation.

You can also store CLI commands in scripts and run them in batch. The thing is, as every management instrument, the CLI follows an all-or-nothing approach.

for example, consider the following command to define a JDBC Driver:

/subsystem=datasources/jdbc-driver=postgres:add(driver-name="postgres",driver-module-name="org.postgres",driver-class-name=org.postgresql.Driver)

Provided that the module is available, it will add a JDBC driver, if that driver does exist. If the resource already exists, the statement will fail.

You can work-around it by using the “if-else” flow control, available in the CLI:

if (outcome != success) of /system-property=test:read-resource

    /system-property=test:add(value=true)

end-if

That’s a good pattern to manage exceptional cases. If you plan to use it for all resources, it will turn into a cumbersome approach. Also, you might want to update an existing object if it already exists. For example to define/undefine/modify one of its attributes.

If that’s your use case, keep reading!

JCliff is a tool which helps you to improve the management and maintenance of your WildFly/Jboss EAP configuration by pre-processing scripts written in its own JSON-like language. After the pre-processing a difference with your configuration is computed. The differences will result in the following actions:

  • add: Add a new configuration item to WildFly tree
  • modify: Modify an existing item in WildFly tree.
  • remove: Remove an item from WildFly tree.
  • undefine: Undefine item in WildFly tree.
  • listAdd: Add a new element in a list in WildFly tree.
  • listRemove: Remove an element from a WildFly list.
  • reorder: Reorder the attributes of an object

Enough with theory. Let’s get our hands dirty.

Installing JCliff

You can either install jcliff using rpm as follows

sudo cat <<EOF >/etc/yum.repos.d/jcliff.repo
[jcliff]
baseurl = http://people.redhat.com/~rpelisse/jcliff.yum/
gpgcheck = 0
name = JCliff repository
EOF

And then:

$ sudo dnf install jcliff

Or you can simply download the binary release from: https://github.com/bserdar/jcliff/releases

Once downloaded and unzipped, add JCLIFF_HOME to the PATH of your OS:

export JCLIFF_HOME=/home/francesco/tools/jcliff-2.12.6
export PATH=${JCLIFF_HOME}:${PATH}

You also need to set the JBOSS_HOME to the location of your WildFly installation:

export JBOSS_HOME=/home/jboss/wildfly-21.0.1.Final/

Scripting with JCliff

Now we are all set to write our first JCliff scripts. The first one, will set into the System Property the variable “jcliff.enabled” to “true”. Edit a file named “enable-jcliff.jcliff”:

{ "system-property" => {
  "jcliff.enabled" => "true",
  }
}

As you can see, the model of jcliff script is pretty much the same of the command :read-resource issues against an application server resource.

Now run it:

$ jcliff enable-jcliff.jcliff 
Jcliff version 2.12.6
2020-12-17 09:51:18:0857: /core-service=platform-mbean/type=runtime:read-attribute(name=system-properties)
2020-12-17 09:51:20:0883: /system-property=jcliff.enabled:add(value="true")

So the command printed in the logs has been executed on the application server. You can check from the configuration that is now included in the properties:

    <system-properties>
        <property name="jcliff.enabled" value="true"/>
    </system-properties>

If you try running again the same command, you will see that just a read resource will be performed. As the variable is already defined, nothing more happens:

$ jcliff enable-jcliff.jcliff 
Jcliff version 2.12.6
2020-12-17 09:51:18:0857: /core-service=platform-mbean/type=runtime:read-attribute(name=system-properties)

Now let’s be a bit more adventurous! We will add a datasource. For the sake of simplicity, we will use an H2 datasource.

Edit the following datasource.jcliff:

{ "datasource" => {
    "ExtraDS" => {
            "connection-url" => "jdbc:h2:mem:test",
            "driver-name" => "h2",
            "enabled" => true,
            "jndi-name" => "java:jboss/datasources/OtherDS",
            "password" => "sa",
            "user-name" => "sa",
            "background-validation-millis" => "300000"
      }
   }
}

So it’s pretty straightforward. We have defined a datasource object whose name is “ExtraDS”. We also have set a minimal set of attributes for it.

When we run the above script, as the resource is not in our configuration it will be added:

$ jcliff datasource.jcliff 

Jcliff version 2.12.6
2020-12-17 11:14:03:0116: /subsystem=datasources:read-children-resources(child-type=data-source)
2020-12-17 11:14:05:0021: data-source add --name=ExtraDS --jndi-name="java:jboss/datasources/OtherDS" --driver-name="h2" --connection-url="jdbc:h2:mem:test"
if result.value==false of /subsystem=datasources/datasource=ExtraDS:read-resource 
data-source enable --name=ExtraDS
:reload

That results in the following element added in our configuration:
<datasource jndi-name="java:jboss/datasources/ExtraDS" pool-name="ExampleDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
    <connection-url>jdbc:h2:mem:test</connection-url>
    <connection-property name="test2">
        testValue
    </connection-property>
    <driver>h2</driver>
    <security>
        <user-name>sa</user-name>
        <password>sa</password>
    </security>
    <validation>
        <background-validation-millis>300000</background-validation-millis>
    </validation>
</datasource>

Running again the datasource will result in a simple read-resource operation. However, let’s change one of the attributes in the script, for example change the JNDI binding:

{ "datasource" => {
    "ExtraDS" => {
            "connection-url" => "jdbc:h2:mem:test",
            "driver-name" => "h2",
            "enabled" => true,
            "jndi-name" => "java:jboss/datasources/ExtraDS",
            "password" => "sa",
            "user-name" => "sa",
            "background-validation-millis" => "300000"
      }
   }
}

Now, an update of the configuration will be performed:

$ jcliff datasource.jcliff 
Jcliff version 2.12.6
2020-12-17 11:16:48:0182: /subsystem=datasources:read-children-resources(child-type=data-source)
2020-12-17 11:16:50:0212: /subsystem=datasources/data-source=ExtraDS:write-attribute(name=jndi-name,value="java:jboss/datasources/ExtraDS")

Conclusion

JCliff is a great tool to improve the maintenance of your WildFly resources and promotes the usage of versioning your configuration which can be therefore saved on Github or wherever you like.

Additionally, JCliff has been integrated into Ansible as part of its own JCliff Collection, please refers to this project documentation (https://github.com/wildfly-extras/ansible_collections_jcliff)

Also, you can integrate it with other tools like puppet to lay down configuration files, and then have puppet execute JCliff. This way, you can define datasources, logging, etc. using puppet code and templates, and configure only what’s necessary.

Thanks to Andrew Block for sharing a first insight on JCliff on https://developers.redhat.com/blog/2019/11/06/managing-jboss-eap-wildfly-using-jcliff/

Found the article helpful? if so please follow us on Socials