One Toml to rule the(m) whole Micro Integrator

Arunan Sugunakumar
Think Integration
Published in
9 min readOct 17, 2020

--

Configuring an enterprise product for a first time use is a tremendous task. You need to go through multiple documentations and resources to pick the right configuration that suits your use case and environment. Most of the enterprise applications used to have multiple configurations files all over the place and configuration management was a major task for the product administrator. If you are familiar with traditional WSO2 products, you might know that there are several files that you have to manage to get everything done.

If you take the traditional WSO2 ESB, you would have seen multiple configuration files inside the conf directory.

conf/
├── axis2
├── carbon.properties
├── carbon.xml
├── cipher-standalone-config.properties
├── data-bridge
├── datasources
├── datasources.properties
├── email
├── etc
├── event-broker.xml
├── event-processor.xml
├── file.properties
├── hl7.properties
├── identity
├── internal-apis.xml
├── jndi.properties
├── log4j2.properties
├── multitenancy
├── nhttp.properties
├── passthru-http.properties
├── registry.xml
├── synapse-handlers.xml
├── synapse.properties
├── tenant-mgt.xml
├── tomcat
├── user-mgt.xml
└── wso2-log-masking.properties

The above approach has the following drawbacks.

  • Enabling a single functionality requires editing multiple configuration files.
  • When migrating from one version to a newer version, the user has to manually migrate all the configuration files by taking a diff.
  • In the container model, managing multiple configuration files is a hazard.

With Micro Integrator, the users do not have to bother about multiple files. There is one and only file that we need to consider about and it is called deployment.toml. It will be used to manage all configuration files internally. All the configuration files will still be there but you do not have to be bothered about it.

[server]
hostname = "localhost"
[user_store]
type = "read_only_ldap"
[keystore.tls]
file_name = "wso2carbon.jks"
password = "wso2carbon"
alias = "wso2carbon"
key_password = "wso2carbon"
[truststore]
file_name = "client-truststore.jks"
password = "wso2carbon"
alias = "symmetric.key.value"
algorithm = "AES"

The above is the default configuration file called deployment.toml that is shipped with Micro Integrator 1.2.0 (Enterprise Integrator 7.1.0) at the moment. Here you can see that only the essentials that are needed to start the server are defined. Based on the use case, you can add more and more configurations that you need. When you are running the Micro Integrator in a Container environment or in a centralized environment, you only need to consider about this Single Toml configuration file. If you can master it, you will rule the Micro Integrator as the title says.

What happened to all the old files?

The files are still there. But you do not have to worry about them. You can forget about their existence and continue working with deployment.toml file. Even if you go and edit the files (as you did with EI 6xx products), the files will be overridden in the server startup and will be replaced with the values that are defined in the deployment.toml or the default.json.

I’ll be including some useful configurations in the latter part of this blog. Before that, I’ll explain some useful features with this model that you can incorporate in your deployments.

To download Micro Integrator, you can either visit https://wso2.com/integration/ or https://wso2.com/integration/micro-integrator/ and download the Zip Archive. Inside the zip, you will find the micro-integrator folder which contains the runtime. I will be referring to this folder as MI-HOME from here onwards.

Reading system variables

You can read values from system variables to the deployment.toml file.

[server]
hostname = "localhost"
offset = "$sys{custom_offset}"

When starting the server, you can pass the system variable like below.

./bin/micro-integrator.sh -Dcustom_offset=2

Reading environment variables

Similar to the system variables you can also read environment variables to the deployment.toml.

[server]
hostname = "localhost"
offset = "$env{custom_offset}"

Set the environment variable and start the server. It would pick the value from the environment variable.

export custom_offset=5
./bin/micro-integrator.sh

These kinds of features come in handy if you are maintaining several environments like Development, QA, and Production. You can use a different set of configuration values for different environments. Also, you can use this in containers in which editing the configuration file is a problem. You can burn the image with these kinds of placeholders, and you can start up a container by passing the environment variables.

Configuring the embedded Micro Integrator in Integration Studio

As I mentioned in my previous blog, there is an embedded Micro Integrator inside the Integration Studio. All the design and development of the services should happen through the Integration Studio.

To configure the embedded runtime, you do not have to go and edit the file directly. You can do it directly from the Studio itself. There are two ways.

  • Opening the configuration window directly from the toolbar.
Configuration icon in the toolbar
  • Opening the configuration file while trying to run the artifacts on the embedded runtime.
Opening the Configuration while running the CAR in embedded runtime

When the Configuration window opens up, you can change the configurations as you wish.

Embedded Micro Integrator Configuration

Please note that the above images were taken from Integration Studio 7.1.0. It might be different in other versions. To download Integration Studio, please visit the website.

Creating a docker image with a modified configuration in Integration Studio

It is possible to create MI based Docker images in Integration Studio with your artifacts on it. In such cases, you might want to change the default configuration file before burning the image. When you create a Docker Exporter Project in Integration Studio, you get a default deployment.toml file and you can change this file as you want. This will be the file that will be copied inside the MI folder when you create the Docker image.

Docker Exporter Project
Changing the default toml file that will be used inside the image

An intuitive way to derive the configuration name

For those of you are familiar with the existing configuration files in WSO2 Enterprise Integration / ESB, you might have trouble finding the exact key name for the configuration you want to match unless you want to go through the documentation. Unfortunately, the old key names and the new key names do not exactly match. The recommended way is to go through the official documentation and figure out the key names.

If you have limited time and you want to find the key name quickly, there is a way to derive the key name. For all the configuration files, there is a .j2 file available inside ‘<MI_HOME>/repository/resources/conf/templates/conf/’. This is a template file that maps each configuration to a key in the deployment.toml.

Let’s say you want to change the server offset in carbon.xml. The offset will be found inside carbon.xml under <Ports> tag. The default value is 10.

<Ports>
<Offset>10</Offset>
</Ports>

If you want to change this value to, say 20, you need to add it inside the deployment.toml. But you don’t know the key to this field. That’s where the template file comes in. Open up carbon.xml.j2 inside <MI_HOME>/repository/resources/conf/templates/conf/. There you would find the below entry for the offset port.

<Ports>
<Offset>{{server.offset | default('10')}}</Offset>
</Ports>

As you can see, the key value is server.offset. The last part after the trailing period would come as the key name and the part before that would come as the section title. That means, inside deployment.toml, you should define value 20 for the key offset under server section like below. (If the server section is already defined, you should define under that section)

[server]
offset = 20

If you have not defined any value for offset in deployment.toml, it would take the default value from default.json which is found inside <MI_HOME>/repository/resources/conf/templates/conf/.

Sometimes, this is not straightforward. Let’s say that you want to enable JMS listener for ActiveMQ in Micro Integrator. You need to define the necessary parameters to make the connection. One such configuration is the QueueConnectionFactory. Below is how you would have defined in old versions of Enterprise Integrator.

<parameter name="myQueueConnectionFactory" locked="false">
<parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>
...
</parameter>

If you look at the axis2.xml.j2 (found inside <MI_HOME>/repository/resources/conf/templates/conf/axis2/), you can find the following section.

{% if transport.jms.listener_enable is defined %}
{% if transport.jms.listener_enable and transport.jms.listener|length==0 %}
<transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener"/>{% elif transport.jms.listener_enable %}<transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">{% for listener in transport.jms.listener %}
<parameter name="{{listener.name}}" locked="false">
{% for name,value in listener.parameter.items() %}
<parameter name="{{name}}" locked="false">{{value}}</parameter>
{% endfor %}
</parameter>
{% endfor %}
</transportReceiver>
{% endif %}

This says that you need to enable JMS listener by defining ‘transport.jms.listener_enable’ inside deployment.toml. Like we did above, we can do this by defining the section and the key name.

[transport.jms]
listener_enable = true

Furthermore, if you need to define the QueueConnectionFactory, you need to add an array of parameters inside deployment.toml under the section transport.jms.listener. Also, it should contain a name key.

[[transport.jms.listener]]name = "myQueueConnectionFactory"
parameter.initial_naming_factory = "org.apache.activemq.jndi.ActiveMQInitialContextFactory"
parameter.provider_url = "tcp://localhost:61616"

Note the double square brackets surrounding transport.jms.listener. It indicates that this section can be defined multiple times with different name key. You might be wondering, how did parameter.initial_naming_factory got mapped to java.naming.factory.initial. Technically the key should have been parameter.’java.naming.factory.initial’. To make the key name shorter, there is another mapping file found inside <MI_HOME>/repository/resources/conf/ called key-mappings.json. There you can find the below entry.

"transport.jms.listener:parameter.initial_naming_factory":  "transport.jms.listener:parameter.'java.naming.factory.initial'"

Because of this mapping, you are not required to enter the full key name inside deployment.toml. This helps to reduce the complexity of the key names.

Another important file that you should take note of is infer.json. As the name suggests, this file is used to infer values for parameters based on a key defined. For example, if the user_store type is defined as a database in deployment.toml, all the parameters related to the database user-store will be inferred from this file.

I would like to emphasize again on the point that you do not have to worry about these files at all. The only goal you should have is to master the deployment.toml configurations and you are good to go.

In this section, I have provided some usages of highly used configurations in Micro Integrator.

Please note that the following configurations are tested with Micro Integrator 1.2.0 and if you are using a different version, use the official documentation to get the correct configuration.

Configuring transport

[transport.jms]
sender_enable = true
[[transport.jms.sender]]
name = "myQueueSender"
parameter.initial_naming_factory="org.apache.activemq.jndi.ActiveMQInitialContextFactory"
parameter.provider_url = "tcp://localhost:61616"
parameter.connection_factory_name = "QueueConnectionFactory"
parameter.connection_factory_type = "queue"
parameter.cache_level = "producer"

Configuring message builders/formatters

[message_formatters]
multipart_form_data="org.apache.axis2.transport.http.MultipartFormDataFormatter"
application_xml="org.apache.axis2.transport.http.ApplicationXMLFormatter"
[message_builders]
application_xml="org.apache.axis2.builder.ApplicationXMLBuilder"
multipart_form_data="org.apache.axis2.builder.MultipartFormDataBuilder"

Configuring a JDBC user-store

[user_store]
class="org.wso2.micro.integrator.security.user.core.jdbc.JDBCUserStoreManager"
type = "database"
[internal_apis.file_user_store]
enable = false
[[datasource]]
id = "WSO2_USER_DB"
url= "jdbc:mysql://localhost:3306/userDB"
username="root"
password="root"
driver="com.mysql.cj.jdbc.Driver"
# if the id of your datasource is WSO2CarbonDB, then following is not needed[realm_manager]
data_source = "WSO2_USER_DB"

Configuring coordination

[[datasource]]
id = "WSO2_COORDINATION_DB"
url= "jdbc:mysql://localhost:3306/clusterDB"
username="root"
password="root"
driver="com.mysql.jdbc.Driver"

Configuring a key store

Primary keystore

[keystore.primary]  
file_name = "wso2carbon.jks"
type = "JKS"
password = "wso2carbon"
alias = "wso2carbon"
key_password = "wso2carbon"

Internal keystore

[keystore.internal]  
file_name = "wso2carbon.jks"
type = "JKS"
password = "wso2carbon"
alias = "wso2carbon"
key_password = "wso2carbon"

Configuring a transaction store

[transaction_counter]
enable = true
data_source = "WSO2_TRANSACTION_DB"
update_interval = 2
[[datasource]]
id = "WSO2_TRANSACTION_DB"
url= "jdbc:mysql://localhost:3306/transactionSummaryDB"
username="root"
password="root"
driver="com.mysql.cj.jdbc.Driver"

Configuring master datasource

[[datasource]] 
id = "WSO2_CARBON_DB"
url="jdbc:h2:./repository/database/WSO2CARBON_DB;DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=60000"
username="username"
password="password"
driver="org.h2.Driver"
pool_options.maxActive=50
pool_options.maxWait = 60000 # wait in milliseconds pool_options.testOnBorrow = true

Configuring a synapse property

[mediation]
synapse.enable_xml_nil = false
synapse.build_valid_nc_name = false
synapse.enable_auto_primitive = true
synapse.enable_namespace_declaration = false

Configuring a passthru-http property

[transport.http] 
core_worker_pool_size = 400 # inferred default: 400 max_worker_pool_size = 400 # inferred default: 400 worker_pool_queue_length = -1

Configuring secrets

[secrets]
secret_key = "[secret value]"

Note : I will be writing a separate blog to explain secrets in length.

Please note that editing log4j2 configurations is not supported via deployment.toml. You need to edit the file directly, or you can use the management APIs/dashboard.

--

--

Arunan Sugunakumar
Think Integration

Software Engineer (Integration) @wso2, Graduated from University of Moratuwa (Computer Science & Eng) and GSOCer @intermineorg