Chapter 4. Connection Management

As we have seen so far, Cloud Connectors abstract away most of the fuss of dealing with APIs: we haven’t needed to know whether a service provider is a REST-based service, a SOAP-based service, or some other custom protocol on top of TCP. But when service providers have more stateful protocols that require the management and sharing of connections, they may need some additional input from the end user of the connector to manage its state.

To put connection management into context, many early APIs, and some current APIs as well, support session-based authentication. In these schemes, the user typically first has to call a “login” method, which takes a username/password combination as input and returns a unique session key that represents that user’s session. The user must then include this session key in each request to the API, and then call “logout” when he is finished. Figure 4-1 illustrates a typical session-based authentication scheme.

This type of authentication maps very naturally to a website because users are accustomed to “logging in” when they start working with a particular site and “logging out” when they are done. But when it comes to web APIs, this proves more difficult because the client must keep track of state, manage concurrent sessions, worry about session timeouts, etc.

Instead, Cloud Connectors abstract away these types of processes and offer automatic connection management that will handle connecting, disconnecting, validating connections, getting a session identifier, and so on. This chapter will discuss in detail how to configure connection management features and fine tune your connectors for both performance and reliability.

Session-based authentication
Figure 4-1. Session-based authentication

Configuring Connection Management

Connection management enabled connectors provide specific settings and configuration to enable both multi-tenancy and the management and sharing of connections.

Pooling Connections

Connection pooling is an important concept in most development environments because the performance cost of establishing a connection to another data source, such as a database, is relatively high. This is no different when using web APIs; creating one or more new connections for every request creates an unnecessary burden and leads to weaker performance.

Take Salesforce, for example. Salesforce offers a full range of APIs, including SOAP, REST, BULK, METADATA, and APEX. The SOAP API authenticates API calls using a typical session-based authentication scheme via a login() operation and a logout() operation, and returns a unique session for each user account. Calling these login and logout methods every time you work with the API wastes resources. Instead, you should maintain a pool of connections and reuse these sessions when possible. However, this brings its own challenges: maintaining a connection pool, validating connections, removing expired connections, etc.

Cloud Connectors do exactly this and abstract away the complexity of maintaining your own pool of connectors. Example 4-1 demonstrates the use of the Salesforce Cloud Connector with the Salesforce SOAP API.

Example 4-1. Salesforce session example
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
    xmlns:sfdc="http://www.mulesoft.org/schema/mule/sfdc"
    xmlns:spring="http://www.springframework.org/schema/beans"
    xmlns:core="http://www.mulesoft.org/schema/mule/core"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/sfdc
        http://www.mulesoft.org/schema/mule/sfdc/current/mule-sfdc.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-current.xsd
        http://www.mulesoft.org/schema/mule/core
        http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

    <sfdc:config
        name="Salesforce"
        username="${salesforce.username}"
        password="${salesforce.password}"
        securityToken="${salesforce.securityToken}" />

    <flow name="salesforceSession">
        <http:inbound-endpoint host="localhost" port="8081"
            path="salesforce" /> 
        <sfdc:get-user-info
            config-ref="Salesforce" />
    </flow>
</mule>

This configuration uses the get-user-info operation that queries the Salesforce SOAP API to simply return the information for the current user. As you can see from the flow at the end of the example, there is no need to specifically call a “login” operation to start using the API or a “logout” operation when finished. Instead, we simply use a connector operation to query the API as usual.

This configuration will automatically manage the Salesforce session for the specified user, logging in and out and reusing the session ID on future invocations, thus saving both time and resources when connecting to the API each time.

Connection Parameters

In order to pool connections, connectors require certain connection parameters to be declared for establishing connections and borrowing and returning connections within the pool. The Salesforce connector uses the following three parameters: username, password, and securityToken. These parameters are required by the connection management features to establish each and every connection. Behind the scenes, it will also use one of these parameters as the key to the connection pool—in this case, the username parameter. When you execute a connector operation, the connector will attempt to look up an already established connection using this key. If none exists, the connector will create one. Once complete, it will return it to the connection pool.

As we have set the connection parameters at the config level, they will be used for all operations and you will always have at most one user session. To support multiple user sessions, each connector that supports connection management also allows you to specify connection parameters as arguments to individual connector operations and will override whatever is specified at the config level. When used at the operation level (i.e., not in config), the connectors allow you to pool multiple sessions and benefit from full expression evaluation so you can do things like extract connection details from an incoming Mule message and thus create new connections with the API at runtime. Example 4-2 amends the previous example, configuring the connection parameters at the operation level opposed to the config level.

Example 4-2. Operation level connection parameters
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
    xmlns:sfdc="http://www.mulesoft.org/schema/mule/sfdc"
    xmlns:spring="http://www.springframework.org/schema/beans"
    xmlns:core="http://www.mulesoft.org/schema/mule/core"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/sfdc
        http://www.mulesoft.org/schema/mule/sfdc/current/mule-sfdc.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-current.xsd
        http://www.mulesoft.org/schema/mule/core
        http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

    <sfdc:config name="Salesforce" />

    <flow name="salesforceSession">
        <http:inbound-endpoint host="localhost" port="8081"
            path="salesforce" />
            
        <sfdc:get-user-info
            username="${salesforce.username}"
            password="${salesforce.password}"
            securityToken="${salesforce.securityToken}"
            config-ref="Salesforce" />
    </flow>
</mule>

If you run this operation twice in quick succession, you will see that the same session ID is logged and that the connector instance is reused. If you allow time for the connection to become idle and rerun the flow, you should see a new session ID and a new instance of the connector. The following example repeats the get-user-info operation to demonstrate the creation and reuse of connections:

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
    xmlns:sfdc="http://www.mulesoft.org/schema/mule/sfdc"
    xmlns:spring="http://www.springframework.org/schema/beans"
    xmlns:core="http://www.mulesoft.org/schema/mule/core"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/sfdc
        http://www.mulesoft.org/schema/mule/sfdc/current/mule-sfdc.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-current.xsd
        http://www.mulesoft.org/schema/mule/core
        http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

    <sfdc:config name="Salesforce" />

    <flow name="salesforceSession">
        <http:inbound-endpoint host="localhost" port="8081"
            path="salesforce" />
            
        <sfdc:get-user-info
            username="bob"
            password="${salesforce.password}"
            securityToken="${salesforce.securityToken}"
            config-ref="Salesforce" />

        <sfdc:get-user-info
            username="ryan"
            password="${salesforce.password}"
            securityToken="${salesforce.securityToken}"
            config-ref="Salesforce" />

        <sfdc:get-user-info 
            username="bob"
            password="${salesforce.password}"
            securityToken="${salesforce.securityToken}"
            config-ref="Salesforce" />
    </flow>
</mule>

The first invocation of the connector passes in the user bob, and as discussed this will create a new connector with the key bob and add it to the pool. It will first call the connect method and then execute the operation.

On the second invocation of the connector, the user passed in is ryan. In this case, Mule will check the connector pool to look for the key ryan and not find one, so it will create a new instance of the connector, add it to the pool with the key ryan, call the connect method, and execute the operation.

The third invocation reuses the connection key bob. When Mule checks the pool this time, it will find an instance of bob and reuse that connector. It will not call the connect method, but instead just execute the message processor and then return it to the pool. This is where the example becomes more efficient than the earlier ones. Because it is already connected, it can omit connecting to the service and retrieving a session key, and instead just use the session key from the previous invocation.

Fine-Tuning the Pool

Connection management is handled for you by default, but under some circumstances you might want to tune connection pooling to meet your organizational requirements. Salesforce allows administrators to configure the amount of user sessions, specify timeout values, impose restrictions on the maximum amount of API calls per day or per user, and so on.

For these types of circumstances, and to enable fine-tuning of the pool, the user of the connector can configure the pool by adding a connection-pooling-profile to the connector configuration, as shown in Example 4-3. This enables the user to configure the maximum amount of user sessions that can run concurrently, how long a session can be idle, etc.

Example 4-3. Fine-tuning a connector’s connection pool
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
    xmlns:sfdc="http://www.mulesoft.org/schema/mule/sfdc"
    xmlns:spring="http://www.springframework.org/schema/beans"
    xmlns:core="http://www.mulesoft.org/schema/mule/core"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/sfdc
        http://www.mulesoft.org/schema/mule/sfdc/current/mule-sfdc.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-current.xsd
        http://www.mulesoft.org/schema/mule/core
        http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

    <sfdc:config name="Salesforce"
        username="${salesforce.username}"
        password="${salesforce.password}"
        securityToken="${salesforce.securityToken}">
        <sfdc:connection-pooling-profile
            maxActive="10"
            maxIdle="10"
            exhaustedAction="WHEN_EXHAUSTED_GROW"
            maxWait="120" />
    </sfdc:config>

    <flow name="salesforceSession">
        <http:inbound-endpoint host="localhost" port="8081"
            path="salesforce" />
        <sfdc:get-user-info
            config-ref="Salesforce" />
    </flow>
</mule>

A connector’s pooling profile configures its component pool. The most important setting is maxActive, which specifies the maximum number of instances of the connector that Mule will create to handle simultaneous requests. When maxActive is exceeded, the pool is said to be exhausted. The full list of configuration follows:

exhaustedAction

Specifies the behavior of the Mule component pool when the pool is exhausted. Possible values are WHEN_EXHAUSTED_FAIL (throws a NoSuchElementException when you already are using the maximum number of sessions), WHEN_EXHAUSTED_WAIT (blocks by invoking Object.wait(long) until a new or idle object is available), and WHEN_EXHAUSTED_GROW (creates a new Mule instance and returns it, essentially making maxActive meaningless). If a positive maxWait value is supplied, it will block for at most that many milliseconds, after which a NoSuchElementException will be thrown. A negative maxThreadWait will block indefinitely.

initialisationPolicy

Determines how components in a pool should be initialized. The possible values are INITIALISE_NONE (will not load any components into the pool on startup), INITIALISE_ONE (will load one initial component into the pool on startup), and INITIALISE_ALL (will load all components in the pool on startup).

maxActive

Controls the maximum number of Mule components that can be borrowed from a session at one time. When set to a negative value, there is no limit to the number of components that may be active at one time.

maxWait

Specifies the number of milliseconds to wait for a pooled component to become available when the pool is exhausted and the exhaustedAction is set to WHEN_EXHAUSTED_WAIT.

Reconnection Strategies

Reconnection strategies specify how a connector behaves when its connection fails. Some connectors offer automatic retries for certain operations. There are a couple of situations in which a retry may solve the problem at hand—for example, if the system is currently busy or if the session has expired.

One situation that can produce problems involves timeouts. For example, an administrator of a Salesforce.com organization can set the session timeout for their users. This session timeout setting is not exposed through the API, which makes it difficult to predict whether the session ID for a connection that you haven’t used for a while is still valid. So if a connection is left idle, calling the API using an idle session ID will result in an error. A new session key needs to be retrieved and the API operation retried.

On top of timeouts, not only can another application logging in with the same user account kill your session by calling logout() , it can also keep your session ID valid by using the API when your application is idle.

These kinds of situations are solvable by reacquiring a connection and retrying the operation. Cloud Connectors will automatically detect invalid session ID errors, and allow you to log in again, retrieve a new session, and retry the operation. They allow you to customize and better control the behavior of a failed connection, by specifying a number of criteria such as:

  • The type of exception

  • The number and frequency of reconnection attempts

  • The notifications generated, and more

In order to configure this functionality, Mule provides a set of three reconnection strategies. Each reconnection strategy can be configured as a child element of a connector’s global config declaration. The following sections detail how to configure each reconnection strategy. More information on available and custom reconnection strategies can be found here.

Warning

Reconnection strategies were previously called retry policies and were an EE (Enterprise Edition)-only feature. Since Mule version 3.3.0, the feature was also made available to CE (Community Edition) users as well, which means that CE Cloud Connectors like Salesforce can take advantage of these features. Prior to this, Cloud Connectors had their own implementation of reconnection strategies via a retryMax attribute at the operation level:

<sfdc:get-session-id retryMax="5" config-ref="Salesforce" /> 

The retryMax argument specifies how many times the specific operation should be retried. The operation is only retried under known conditions specified by the developer of the connector and isn’t open to customization by the end user of the connector. This functionality has since been deprecated in favor of the common Mule reconnection strategies.

Standard Reconnection Strategy

The standard reconnection strategy, reconnect, is a reconnection strategy that allows the user to configure how many times a reconnection should be attempted and how long to wait between attempts. Example 4-4 demonstrates configuring a standard reconnection strategy to define how many times a reconnection should be attempted and the frequency between each attempt.

Example 4-4. Standard reconnection strategy
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
    xmlns:sfdc="http://www.mulesoft.org/schema/mule/sfdc"
    xmlns:spring="http://www.springframework.org/schema/beans"
    xmlns:core="http://www.mulesoft.org/schema/mule/core"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/sfdc
        http://www.mulesoft.org/schema/mule/sfdc/current/mule-sfdc.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-current.xsd
        http://www.mulesoft.org/schema/mule/core
        http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

    <sfdc:config
        name="Salesforce"
        username="${salesforce.username}"
        password="${salesforce.password}"
        securityToken="${salesforce.securityToken}">
        <reconnect count="5" frequency="1000" />
    </sfdc:config>

    <flow name="salesforceSession">
        <http:inbound-endpoint host="localhost" port="8081"
            path="salesforce" />
            
        <sfdc:get-user-info
            config-ref="Salesforce" />
    </flow>
</mule> 

This example amends the previous configuration to use the standard reconnect strategy. The most important setting here is the count attribute that defines how many times a reconnection should be attempted. The full list of configuration follows:

blocking

A boolean value to determine whether the reconnection strategy will run in the current thread or if false, run in a separate, non-blocking thread

frequency

How often (in milliseconds) to wait between reconnection attempts

count

How many times a reconnection should be attempted

Using the current configuration, the connector will retry up to a maximum of five times and at a frequency of one second using the count and frequency attributes, respectively. If for any reason a session becomes invalid or the service becomes unavailable, the operation will now attempt to retry the operation up to five times before eventually throwing an exception if unsuccessful.

Reconnect Forever Strategy

The reconnect forever strategy, reconnect-forever, is a reconnection strategy that retries an infinite number of times at the specified frequency. This strategy is very similar to the previous reconnect strategy, but with the subtle difference of not setting a count attribute to limit the maximum amount of retries. Example 4-5 demonstrates this approach.

Example 4-5. Reconnect forever strategy
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
    xmlns:sfdc="http://www.mulesoft.org/schema/mule/sfdc"
    xmlns:spring="http://www.springframework.org/schema/beans"
    xmlns:core="http://www.mulesoft.org/schema/mule/core"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/sfdc
        http://www.mulesoft.org/schema/mule/sfdc/current/mule-sfdc.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-current.xsd
        http://www.mulesoft.org/schema/mule/core
        http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

    <sfdc:config
        name="Salesforce"
        username="${salesforce.username}"
        password="${salesforce.password}"
        securityToken="${salesforce.securityToken}">
        <reconnect-forever frequency="1000" />
    </sfdc:config>

    <flow name="salesforceSession">
        <http:inbound-endpoint host="localhost" port="8081"
            path="salesforce" />
            
        <sfdc:get-user-info
            config-ref="Salesforce" />
    </flow>
</mule> 

This example amends the previous configuration to use the reconnect-forever strategy. The most important setting here is the frequency attribute that defines how long to wait between reconnection attempts. This configuration no longer requires the count attribute as it will retry indefinitely. The full list of configuration follows:

blocking

A boolean value to determine whether the reconnection strategy will run in the current thread or if false, run in a separate, non-blocking thread

frequency

How often (in milliseconds) to wait between reconnection attempts

Custom Reconnection Strategy

The custom reconnection strategy, reconnect-custom-strategy, is a custom, user-defined reconnection strategy that allows you to take specific action based on the type of exception and decide whether or not the connection should be retried. Example 4-6 demonstrates this approach.

Example 4-6. Custom reconnection strategy
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
    xmlns:sfdc="http://www.mulesoft.org/schema/mule/sfdc"
    xmlns:spring="http://www.springframework.org/schema/beans"
    xmlns:core="http://www.mulesoft.org/schema/mule/core"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/sfdc
        http://www.mulesoft.org/schema/mule/sfdc/current/mule-sfdc.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-current.xsd
        http://www.mulesoft.org/schema/mule/core
        http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

    <sfdc:config
        name="Salesforce"
        username="${salesforce.username}"
        password="${salesforce.password}"
        securityToken="${salesforce.securityToken}">
        <reconnect-custom-strategy 
            class="org.mule.retry.policies.NoRetryPolicyTemplate" />
    </sfdc:config>

    <flow name="salesforceSession">
        <http:inbound-endpoint host="localhost" port="8081"
            path="salesforce" />
        <sfdc:get-user-info
            config-ref="Salesforce" />
    </flow>
</mule> 

This example amends the previous configuration to use the reconnect-custom-strategy . The most important setting here is the class attribute that references a class that implements the org.mule.api.retry.RetryPolicy interface. The full list of configuration follows:

blocking

A boolean value to determine whether the reconnection strategy will run in the current thread or if false, run in a separate, non-blocking thread

class

A class that implements the org.mule.api.retry.RetryPolicy interface

Example 4-7 demonstrates a basic class implementing the org.mule.api.retry.RetryPolicy interface, where the method PolicyStatus applyPolicy(Throwable cause) takes some action based on the type of exception, then returns PolicyStatus to indicate whether the policy has been exhausted or should continue to retry. This particular implementation does not attempt a retry at all, and merely acts as a placeholder to demonstrate.

Example 4-7. Custom retry policy
package org.mule.retry.policies;

import org.mule.api.retry.RetryPolicy;
import org.mule.retry.PolicyStatus;

/**
 * This policy is basically a placeholder.  It does not attempt to retry at all.
 */
public class NoRetryPolicyTemplate extends AbstractPolicyTemplate
{
    public RetryPolicy createRetryInstance()
    {
        return new NoRetryPolicy();
    }

    protected static class NoRetryPolicy implements RetryPolicy
    {
        public PolicyStatus applyPolicy(Throwable cause)
        {
            return PolicyStatus.policyExhausted(cause);
        }
    }

    public String toString()
    {
        return "NoRetryPolicy{}";
    }
}

Summary

In this chapter we have briefly discussed stateful APIs and session-based authentication. Building your own connection management around these APIs can be tricky, to say the least: creating and managing your own connection pools, detecting idle sessions, and retrying operations. Cloud Connectors abstract this away, providing a consistent interface for fine-tuning yourself—and finally, no more session drop-outs!

Get Getting Started with Mule Cloud Connect now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.