The DevKit is a tool for accelerating the development of Mule extensions. A popular Mule extension is what we call a Cloud Connector. A Cloud Connector provides Mule with the ability to receive and send messages to/from a cloud service provider. We do not make assumptions about whether that service provider is a REST-based service, a SOAP endpoint or a custom protocol on top of TCP. Having said that, we do offer certain services for some types of service providers. In this post I’m going to be talking about connection management, a feature in the DevKit that provides management and sharing of connections for those service providers that have stateful protocols.
The What and the When
Imagine that you are trying to connect to a service provider that offers the current date and time in different countries. Before you can access that API call, you need to issue a login call, send in your credentials and get a session identifier. The service provider will require you to send that session identifier in the API call to get the date and time.
If you are developing this cloud connector, you might be inclined to do something along these lines:
The above example will work, but logging in and out on every API call is not very efficient. A better solution would be to use the @Start and @Stop lifecycle methods and log in and log out on those as follows:
Now that’s better, isn’t it? There is still a small problem, however, in that you can only store a single session identifier. If you want to support multiple sessions you need something else, and this is where our connection management features kick in. Let’s see how the previous example can be converted to connection management:
The first thing you should notice is the @Connect and @Disconnect annotations on the methods previously annotated with @Start and @Stop. The second thing you should notice is that we removed the @Configurable fields for username and password, and now those are arguments to the @Connect annotation. I will explain why in a second, but first let me tell you why this is different from connecting and disconnecting on @Start and @Stop.
The message processor generated for getDateAndTime in the snippet with @Start and @Stop is completely different from the one generated in the snippet with @Connect and @Disconnect, the difference being that in the @Start and @Stop snippet the message processor only holds a single instance of the POJO and it uses that to call getDateAndTime. The message processor that gets generated with @Connect and @Disconnect does not hold a single instance of the POJO but rather a pool of them. Upon executing it, it will borrow a connector from the pool, execute getDateAndTime and return it back. Starting to see the difference? Each object in the pool represents an individual connection. The beauty of the model is that when you are programming your POJO you don’t have to worry about this. Your POJO still thinks it is dealing with a single connection. It is Mule that will instantiate your POJO multiple times depending on user needs.
@Connect and @ConnectionKey
Let’s go back to the @Connect method for a second. Remember that we removed the two @Configurable (username and password) fields and we made them arguments to the @Connect method. Why did we do that? Well, that is how we tell Mule that those configurable fields are important when establishing a connection. You can still have other @Configurable fields, but Mule will treat the @Connect arguments in a special way.
They can still be used as attributes at the config level of your connector as follows:
And you can also use them on each message processor:
The @Connect arguments can also be specified as arguments to each message processor (they will override whatever you specified on :config). When used at the message processor level (i.e. not in :config), they get the benefit of full expression evaluation so you can do things like this:
You can extract your connection information from the Mule message.
One thing that you may have noticed but I didn’t mention is that one of the arguments is annotated with @ConnectionKey. The argument marked with @ConnectionKey will be used as the key for borrowing and returning objects in the pool. Imagine this:
The last snippet shows three invocations under two different users.
The first invocation (the one that gathers time for New Zealand) will create a new instance of your POJO and call @Connect using johndoe and 123. Once getDateAndTime is done executing the POJO that was created will be returned to the pool. We aren’t calling @Disconnect just yet, so the POJO is returned to the pool while still connected.
Now the second invocations go through. This time around the user is janedoe. Since the username is marked as a @ConnectionKey that is what will be used to retrieve a POJO from the pool (remember that until this point there is only one POJO and that represents a connection for johndoe). Since none exists, Mule will create another instance of your POJO and call @Connect with janedoe and 567. Again, once getDateAndTime finishes executing the POJO that was just created will be return back to the pool while still connected.
The third invocation is where things change, because Mule will look up a connected POJO in the pool using the @ConnectionKey (in this case johndoe) and since one already exists (thanks to the first invocation), that is what will be used. @Connect will not be called again since the POJO is already connected. So in this case you gain some performance because Mule will only connect when it needs to.
Are you still with me? Good. One thing that we still have to cover is @Disconnect. The connections that are in the pool will not stay there forever. They will get evicted once they sit idle for too long (the idle timeout is configurable). Once they get evicted from the pool @Disconnect is called to clean up the connection. The disconnect method should usually call a logout or similar API call on the service provider to let them know that the session or connection is no longer needed.
Configuring the Pool
I mentioned briefly that the idle timeout can be configured, but that is not the only thing that can be configured in the pool. A Mule module that uses connection management gains a special configuration element that allows tight control of how the pool of connections should behave.
The following is a list of available attributes for configuring the pool:
- maxActive: Controls the maximum number of connections that can be borrowed at one time. When set to a negative value, there is no limit to the number of connections that may be active at one time. When maxActive is exceeded, the pool is said to be exhausted.
- maxIdle: Controls the maximum number of connections that can sit idle in the pool at any time. When set to a negative value, there is no limit to the number of connections that may be idle at one time.
- exhaustedAction: Specifies the behavior of the connection pool when the pool is exhausted. Possible values are: WHEN_EXHAUSTED_FAIL, which will throw a NoSuchElementException, WHEN_EXHAUSTED_WAIT, which will block by invoking Object.wait(long) until a new or idle object is available, or WHEN_EXHAUSTED_GROW, which will create a new connection and return 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. If maxThreadWait is a negative value, it will block indefinitely. Default value is: WHEN_EXHAUSTED_GROW.
- maxWait: Specifies the number of milliseconds to wait for a connection to become available when the pool is exhausted and the exhaustedAction is set to WHEN_EXHAUSTED_WAIT.
As you can see, with very little you gain very much, which is the underlying concept behind the DevKit for accelerating development. You can learn more at our website.
See ya next time!