DevKit Feature Spotlight: Connection Management

motif

The is a tool for accelerating the development of Mule extensions. A popular Mule extension is what we call a 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 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.

Expired Connections

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.

Conclusions

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.

Don’t forget to follow us @mulejockey in Twitter if you want to keep up to date with everything DevKit.

See ya next time!


We'd love to hear your opinion on this post


7 Responses to “DevKit Feature Spotlight: Connection Management”

  1. Good post. While the pooling methods described in the body make sense, what if you had a connector which wrapped a thread-safe, non-blocking asynchronous client.

    Ideally this would be treated as a singleton.

    Would it still be using the @Connector annotations, as that implies the use of @Connect and @Disconnect, or would it be a @Module with @Start and @Stop?

    If a connector, what’s the proper setup to ensure only 1 object is created and reused?

    • If the connector wrapped a thread-safe, non-blocking asynchronous client then the connection facilities offered by the DevKit would not make much sense. Seems to me that in the suggested scenario the underlying client would be responsible for managing connections.

      If you are dealing with a single object I would suggest that you manage its lifecycle in @PostConstruct and @PreDestroy.

      @Connector
      public class MyConnectorWrapper
      {
          public UnderlyingConnectorObject myUnderlyingConnector;
      
          @PostConstruct
          public void init() {
          myUnderlyingConnector = new UnderlyingConnectorObject();
          // here you can also call whatever initialization mechanism your
          // underlying connector offers
        }
        
        @PreDestroy
        public void dispose() {
          // also here you can call whatever cleanup mechanism your
          // underlying connector offers
          myUnderlyingConnector = null;
        }
      }

      Makes sense?

  2. And slightly unrelated, what’s the connector manager’s behavior if there is a @Connect method without any explict @ConnectionKey?

    Are new connections created by default, or reused?

    • Right now, the @ConnectionKey is mandatory. At least one field must be an @ConnectionKey.

      In the future we want to make it so that if no @ConnectionKey is specified the developer of the connector still gets the benefits of the pool.

      If you can provide us with a specific use-case we can bump the priority of such a feature.

  3. How do we detect and handle timeouts coming from the far side now that we’re reusing sessions?

    If the far side responds with “Not logged in anymore session expired.” how does the POJO detect this and issue a connect again?

    Or is this somehow handled elsewhere?

    • Interesting that you ask that. There is one more annotation that was omitted from the blog post due to simplicity.

      That annotation is called @InvalidateConnectionOn and it receives a single argument which is a list of exception classes.

      Whenever you use that annotation the DevKit will wrap the @Processor method in a try/catch and it will watch for the exceptions you specify in the annotation. When an exception is catch, it will
      effectively invalidate the connection (dropping it).

      The DevKit also offers automatic retry and reconnect. This means that the DevKit will not only invalidate the connection, but it will also acquire a new one and retry the whole operation.

      The combination of this two features can effectively deal with expires session, by renewing them automatically.

      The following example is an extract of our Salesforce connector code, in which you can see the annotation in action:

          
          @Processor
          @InvalidateConnectionOn(exception = SoapConnection.SessionTimedOutException.class)
          public List<SaveResult> create(@Placement(group = "Type") @FriendlyName("sObject Type") String type,
                                         @Placement(group = "sObject Field Mappings") List<Map<String, Object>> objects) throws Exception {
              return Arrays.asList(connection.create(toSObjectList(type, objects)));
          }
      

      Does this answer your question? Let me know if you have further questions.

  4. If I need to use a cloud connector for its connection management benefits, how can I club it with existing connectors of mule.

    ex:- I need to connect to and external; web service using and outbound soap/HTTP of mule. in this scenario how can i leverage connection management feature of cloud connector.