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.
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.
<?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.
<?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.
<?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 aNoSuchElementException
when you already are using the maximum number of sessions),WHEN_EXHAUSTED_WAIT
(blocks by invokingObject.wait(long)
until a new or idle object is available), andWHEN_EXHAUSTED_GROW
(creates a new Mule instance and returns it, essentially makingmaxActive
meaningless). If a positivemaxWait
value is supplied, it will block for at most that many milliseconds, after which aNoSuchElementException
will be thrown. A negativemaxThreadWait
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), andINITIALISE_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 toWHEN_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.
<?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.
<?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.
<?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.
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.